summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/windows/fs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/windows/fs.rs')
-rw-r--r--library/std/src/sys/windows/fs.rs128
1 files changed, 83 insertions, 45 deletions
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 956db577d..21a65bc25 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -88,6 +88,14 @@ pub struct FilePermissions {
pub struct FileTimes {
accessed: Option<c::FILETIME>,
modified: Option<c::FILETIME>,
+ created: Option<c::FILETIME>,
+}
+
+impl fmt::Debug for c::FILETIME {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;
+ f.debug_tuple("FILETIME").field(&time).finish()
+ }
}
#[derive(Debug)]
@@ -290,6 +298,7 @@ impl File {
ptr::null_mut(),
)
};
+ let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
if let Ok(handle) = handle.try_into() {
Ok(File { handle: Handle::from_inner(handle) })
} else {
@@ -477,7 +486,7 @@ impl File {
fn reparse_point(
&self,
space: &mut Align8<[MaybeUninit<u8>]>,
- ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
+ ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
@@ -496,32 +505,33 @@ impl File {
)
})?;
const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
- Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
+ Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
}
}
fn readlink(&self) -> io::Result<PathBuf> {
- let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+ let mut space =
+ Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe {
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
c::IO_REPARSE_TAG_SYMLINK => {
- let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
- ptr::addr_of!((*buf).rest).cast();
+ let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
+ ptr::addr_of_mut!((*buf).rest).cast();
assert!(info.is_aligned());
(
- ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
+ ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
)
}
c::IO_REPARSE_TAG_MOUNT_POINT => {
- let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
- ptr::addr_of!((*buf).rest).cast();
+ let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
+ ptr::addr_of_mut!((*buf).rest).cast();
assert!(info.is_aligned());
(
- ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
+ ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
(*info).SubstituteNameOffset / 2,
(*info).SubstituteNameLength / 2,
false,
@@ -535,13 +545,20 @@ impl File {
}
};
let subst_ptr = path_buffer.add(subst_off.into());
- let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
+ let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
// Absolute paths start with an NT internal namespace prefix `\??\`
// We should not let it leak through.
if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
- subst = &subst[4..];
+ // Turn `\??\` into `\\?\` (a verbatim path).
+ subst[1] = b'\\' as u16;
+ // Attempt to convert to a more user-friendly path.
+ let user = super::args::from_wide_to_user_path(
+ subst.iter().copied().chain([0]).collect(),
+ )?;
+ Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user))))
+ } else {
+ Ok(PathBuf::from(OsString::from_wide(subst)))
}
- Ok(PathBuf::from(OsString::from_wide(subst)))
}
}
@@ -567,7 +584,10 @@ impl File {
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
- if times.accessed.map_or(false, is_zero) || times.modified.map_or(false, is_zero) {
+ if times.accessed.map_or(false, is_zero)
+ || times.modified.map_or(false, is_zero)
+ || times.created.map_or(false, is_zero)
+ {
return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"Cannot set file timestamp to 0",
@@ -575,14 +595,23 @@ impl File {
}
let is_max =
|t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX;
- if times.accessed.map_or(false, is_max) || times.modified.map_or(false, is_max) {
+ if times.accessed.map_or(false, is_max)
+ || times.modified.map_or(false, is_max)
+ || times.created.map_or(false, is_max)
+ {
return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
));
}
cvt(unsafe {
- c::SetFileTime(self.as_handle(), None, times.accessed.as_ref(), times.modified.as_ref())
+ let created =
+ times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+ let accessed =
+ times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+ let modified =
+ times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+ c::SetFileTime(self.as_raw_handle(), created, accessed, modified)
})?;
Ok(())
}
@@ -611,9 +640,9 @@ impl File {
/// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
fn posix_delete(&self) -> io::Result<()> {
let mut info = c::FILE_DISPOSITION_INFO_EX {
- Flags: c::FILE_DISPOSITION_DELETE
- | c::FILE_DISPOSITION_POSIX_SEMANTICS
- | c::FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
+ Flags: c::FILE_DISPOSITION_FLAG_DELETE
+ | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
+ | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
};
let size = mem::size_of_val(&info);
cvt(unsafe {
@@ -784,15 +813,15 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<
// See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
unsafe {
let mut handle = ptr::null_mut();
- let mut io_status = c::IO_STATUS_BLOCK::default();
- let name_str = c::UNICODE_STRING::from_ref(name);
+ let mut io_status = c::IO_STATUS_BLOCK::PENDING;
+ let mut name_str = c::UNICODE_STRING::from_ref(name);
use crate::sync::atomic::{AtomicU32, Ordering};
// The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
// tricked into following a symlink. However, it may not be available in
// earlier versions of Windows.
static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
- let object = c::OBJECT_ATTRIBUTES {
- ObjectName: &name_str,
+ let mut object = c::OBJECT_ATTRIBUTES {
+ ObjectName: &mut name_str,
RootDirectory: parent.as_raw_handle(),
Attributes: ATTRIBUTES.load(Ordering::Relaxed),
..c::OBJECT_ATTRIBUTES::default()
@@ -800,7 +829,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<
let status = c::NtCreateFile(
&mut handle,
access,
- &object,
+ &mut object,
&mut io_status,
crate::ptr::null_mut(),
0,
@@ -832,6 +861,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<
}
impl AsInner<Handle> for File {
+ #[inline]
fn as_inner(&self) -> &Handle {
&self.handle
}
@@ -985,6 +1015,10 @@ impl FileTimes {
pub fn set_modified(&mut self, t: SystemTime) {
self.modified = Some(t.into_inner());
}
+
+ pub fn set_created(&mut self, t: SystemTime) {
+ self.created = Some(t.into_inner());
+ }
}
impl FileType {
@@ -1132,26 +1166,29 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
&dir,
&name,
c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
- )?;
- dirlist.push(child_dir);
- } else {
- for i in 1..=MAX_RETRIES {
- let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
- match result {
- Ok(f) => delete(&f)?,
- // Already deleted, so skip.
- Err(e) if e.kind() == io::ErrorKind::NotFound => break,
- // Retry a few times if the file is locked or a delete is already in progress.
- Err(e)
- if i < MAX_RETRIES
- && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
- || e.raw_os_error()
- == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
- // Otherwise return the error.
- Err(e) => return Err(e),
- }
- thread::yield_now();
+ );
+ // On success, add the handle to the queue.
+ // If opening the directory fails we treat it the same as a file
+ if let Ok(child_dir) = child_dir {
+ dirlist.push(child_dir);
+ continue;
+ }
+ }
+ for i in 1..=MAX_RETRIES {
+ let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
+ match result {
+ Ok(f) => delete(&f)?,
+ // Already deleted, so skip.
+ Err(e) if e.kind() == io::ErrorKind::NotFound => break,
+ // Retry a few times if the file is locked or a delete is already in progress.
+ Err(e)
+ if i < MAX_RETRIES
+ && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
+ || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
+ // Otherwise return the error.
+ Err(e) => return Err(e),
}
+ thread::yield_now();
}
}
// If there were no more files then delete the directory.
@@ -1357,7 +1394,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
_dwCallbackReason: c::DWORD,
_hSourceFile: c::HANDLE,
_hDestinationFile: c::HANDLE,
- lpData: c::LPVOID,
+ lpData: c::LPCVOID,
) -> c::DWORD {
if dwStreamNumber == 1 {
*(lpData as *mut i64) = StreamBytesTransferred;
@@ -1404,9 +1441,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
let f = File::open(junction, &opts)?;
let h = f.as_inner().as_raw_handle();
unsafe {
- let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+ let mut data =
+ Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
let data_ptr = data.0.as_mut_ptr();
- let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
// Zero the header to ensure it's fully initialized, including reserved parameters.
*db = mem::zeroed();