1 use crate::std::os::windows::prelude::*;
2
3 use crate::std::borrow::Cow;
4 use crate::std::ffi::OsString;
5 use crate::std::fmt;
6 use crate::std::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
7 use crate::std::mem::{self, MaybeUninit};
8 use crate::std::os::windows::io::{AsHandle, BorrowedHandle};
9 use crate::std::path::{Path, PathBuf};
10 use crate::std::ptr;
11 use crate::std::slice;
12 use crate::std::sync::Arc;
13 use crate::std::sys::handle::Handle;
14 use crate::std::sys::time::SystemTime;
15 use crate::std::sys::{c, cvt, Align8};
16 use crate::std::sys_common::{AsInner, FromInner, IntoInner};
17 use crate::std::thread;
18 use dlibc;
19
20 use super::path::maybe_verbatim;
21 use super::to_u16s;
22
23 pub struct File {
24 handle: Handle,
25 }
26
27 #[derive(Clone)]
28 pub struct FileAttr {
29 attributes: c::DWORD,
30 creation_time: c::FILETIME,
31 last_access_time: c::FILETIME,
32 last_write_time: c::FILETIME,
33 file_size: u64,
34 reparse_tag: c::DWORD,
35 volume_serial_number: Option<u32>,
36 number_of_links: Option<u32>,
37 file_index: Option<u64>,
38 }
39
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41 pub struct FileType {
42 attributes: c::DWORD,
43 reparse_tag: c::DWORD,
44 }
45
46 pub struct ReadDir {
47 handle: FindNextFileHandle,
48 root: Arc<PathBuf>,
49 first: Option<c::WIN32_FIND_DATAW>,
50 }
51
52 struct FindNextFileHandle(c::HANDLE);
53
54 unsafe impl Send for FindNextFileHandle {}
55 unsafe impl Sync for FindNextFileHandle {}
56
57 pub struct DirEntry {
58 root: Arc<PathBuf>,
59 data: c::WIN32_FIND_DATAW,
60 }
61
62 unsafe impl Send for OpenOptions {}
63 unsafe impl Sync for OpenOptions {}
64
65 #[derive(Clone, Debug)]
66 pub struct OpenOptions {
67 // generic
68 read: bool,
69 write: bool,
70 append: bool,
71 truncate: bool,
72 create: bool,
73 create_new: bool,
74 // system-specific
75 custom_flags: u32,
76 access_mode: Option<c::DWORD>,
77 attributes: c::DWORD,
78 share_mode: c::DWORD,
79 security_qos_flags: c::DWORD,
80 security_attributes: c::LPSECURITY_ATTRIBUTES,
81 }
82
83 #[derive(Clone, PartialEq, Eq, Debug)]
84 pub struct FilePermissions {
85 attrs: c::DWORD,
86 }
87
88 #[derive(Copy, Clone, Debug, Default)]
89 pub struct FileTimes {
90 accessed: Option<c::FILETIME>,
91 modified: Option<c::FILETIME>,
92 created: Option<c::FILETIME>,
93 }
94
95 impl fmt::Debug for c::FILETIME {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;
98 f.debug_tuple("FILETIME").field(&time).finish()
99 }
100 }
101
102 #[derive(Debug)]
103 pub struct DirBuilder;
104
105 impl fmt::Debug for ReadDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
108 // Thus the result will be e g 'ReadDir("C:\")'
109 fmt::Debug::fmt(&*self.root, f)
110 }
111 }
112
113 impl Iterator for ReadDir {
114 type Item = io::Result<DirEntry>;
next(&mut self) -> Option<io::Result<DirEntry>>115 fn next(&mut self) -> Option<io::Result<DirEntry>> {
116 if let Some(first) = self.first.take() {
117 if let Some(e) = DirEntry::new(&self.root, &first) {
118 return Some(Ok(e));
119 }
120 }
121 unsafe {
122 let mut wfd = mem::zeroed();
123 loop {
124 if c::FindNextFileW(self.handle.0, &mut wfd) == 0 {
125 if c::GetLastError() == c::ERROR_NO_MORE_FILES {
126 return None;
127 } else {
128 return Some(Err(Error::last_os_error()));
129 }
130 }
131 if let Some(e) = DirEntry::new(&self.root, &wfd) {
132 return Some(Ok(e));
133 }
134 }
135 }
136 }
137 }
138
139 impl Drop for FindNextFileHandle {
drop(&mut self)140 fn drop(&mut self) {
141 let r = unsafe { c::FindClose(self.0) };
142 debug_assert!(r != 0);
143 }
144 }
145
146 impl DirEntry {
new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry>147 fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
148 match &wfd.cFileName[0..3] {
149 // check for '.' and '..'
150 &[46, 0, ..] | &[46, 46, 0, ..] => return None,
151 _ => {}
152 }
153
154 Some(DirEntry {
155 root: root.clone(),
156 data: *wfd,
157 })
158 }
159
path(&self) -> PathBuf160 pub fn path(&self) -> PathBuf {
161 self.root.join(&self.file_name())
162 }
163
file_name(&self) -> OsString164 pub fn file_name(&self) -> OsString {
165 let filename = super::truncate_utf16_at_nul(&self.data.cFileName);
166 OsString::from_wide(filename)
167 }
168
file_type(&self) -> io::Result<FileType>169 pub fn file_type(&self) -> io::Result<FileType> {
170 Ok(FileType::new(
171 self.data.dwFileAttributes,
172 /* reparse_tag = */ self.data.dwReserved0,
173 ))
174 }
175
metadata(&self) -> io::Result<FileAttr>176 pub fn metadata(&self) -> io::Result<FileAttr> {
177 Ok(self.data.into())
178 }
179 }
180
181 impl OpenOptions {
new() -> OpenOptions182 pub fn new() -> OpenOptions {
183 OpenOptions {
184 // generic
185 read: false,
186 write: false,
187 append: false,
188 truncate: false,
189 create: false,
190 create_new: false,
191 // system-specific
192 custom_flags: 0,
193 access_mode: None,
194 share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
195 attributes: 0,
196 security_qos_flags: 0,
197 security_attributes: ptr::null_mut(),
198 }
199 }
200
read(&mut self, read: bool)201 pub fn read(&mut self, read: bool) {
202 self.read = read;
203 }
write(&mut self, write: bool)204 pub fn write(&mut self, write: bool) {
205 self.write = write;
206 }
append(&mut self, append: bool)207 pub fn append(&mut self, append: bool) {
208 self.append = append;
209 }
truncate(&mut self, truncate: bool)210 pub fn truncate(&mut self, truncate: bool) {
211 self.truncate = truncate;
212 }
create(&mut self, create: bool)213 pub fn create(&mut self, create: bool) {
214 self.create = create;
215 }
create_new(&mut self, create_new: bool)216 pub fn create_new(&mut self, create_new: bool) {
217 self.create_new = create_new;
218 }
219
custom_flags(&mut self, flags: u32)220 pub fn custom_flags(&mut self, flags: u32) {
221 self.custom_flags = flags;
222 }
access_mode(&mut self, access_mode: u32)223 pub fn access_mode(&mut self, access_mode: u32) {
224 self.access_mode = Some(access_mode);
225 }
share_mode(&mut self, share_mode: u32)226 pub fn share_mode(&mut self, share_mode: u32) {
227 self.share_mode = share_mode;
228 }
attributes(&mut self, attrs: u32)229 pub fn attributes(&mut self, attrs: u32) {
230 self.attributes = attrs;
231 }
security_qos_flags(&mut self, flags: u32)232 pub fn security_qos_flags(&mut self, flags: u32) {
233 // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
234 // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
235 self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;
236 }
security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES)237 pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) {
238 self.security_attributes = attrs;
239 }
240
get_access_mode(&self) -> io::Result<c::DWORD>241 fn get_access_mode(&self) -> io::Result<c::DWORD> {
242 const ERROR_INVALID_PARAMETER: i32 = 87;
243
244 match (self.read, self.write, self.append, self.access_mode) {
245 (.., Some(mode)) => Ok(mode),
246 (true, false, false, None) => Ok(c::GENERIC_READ),
247 (false, true, false, None) => Ok(c::GENERIC_WRITE),
248 (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),
249 (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),
250 (true, _, true, None) => {
251 Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
252 }
253 (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)),
254 }
255 }
256
get_creation_mode(&self) -> io::Result<c::DWORD>257 fn get_creation_mode(&self) -> io::Result<c::DWORD> {
258 const ERROR_INVALID_PARAMETER: i32 = 87;
259
260 match (self.write, self.append) {
261 (true, false) => {}
262 (false, false) => {
263 if self.truncate || self.create || self.create_new {
264 return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
265 }
266 }
267 (_, true) => {
268 if self.truncate && !self.create_new {
269 return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
270 }
271 }
272 }
273
274 Ok(match (self.create, self.truncate, self.create_new) {
275 (false, false, false) => c::OPEN_EXISTING,
276 (true, false, false) => c::OPEN_ALWAYS,
277 (false, true, false) => c::TRUNCATE_EXISTING,
278 (true, true, false) => c::CREATE_ALWAYS,
279 (_, _, true) => c::CREATE_NEW,
280 })
281 }
282
get_flags_and_attributes(&self) -> c::DWORD283 fn get_flags_and_attributes(&self) -> c::DWORD {
284 self.custom_flags
285 | self.attributes
286 | self.security_qos_flags
287 | if self.create_new {
288 c::FILE_FLAG_OPEN_REPARSE_POINT
289 } else {
290 0
291 }
292 }
293 }
294
295 impl File {
open(path: &Path, opts: &OpenOptions) -> io::Result<File>296 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
297 let path = maybe_verbatim(path)?;
298 let handle = unsafe {
299 c::CreateFileW(
300 path.as_ptr(),
301 opts.get_access_mode()?,
302 opts.share_mode,
303 opts.security_attributes,
304 opts.get_creation_mode()?,
305 opts.get_flags_and_attributes(),
306 ptr::null_mut(),
307 )
308 };
309 let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
310 if let Ok(handle) = handle.try_into() {
311 Ok(File {
312 handle: Handle::from_inner(handle),
313 })
314 } else {
315 Err(Error::last_os_error())
316 }
317 }
318
fsync(&self) -> io::Result<()>319 pub fn fsync(&self) -> io::Result<()> {
320 cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
321 Ok(())
322 }
323
datasync(&self) -> io::Result<()>324 pub fn datasync(&self) -> io::Result<()> {
325 self.fsync()
326 }
327
truncate(&self, size: u64) -> io::Result<()>328 pub fn truncate(&self, size: u64) -> io::Result<()> {
329 let mut info = c::FILE_END_OF_FILE_INFO {
330 EndOfFile: size as c::LARGE_INTEGER,
331 };
332 let size = mem::size_of_val(&info);
333 cvt(unsafe {
334 c::SetFileInformationByHandle(
335 self.handle.as_raw_handle(),
336 c::FileEndOfFileInfo,
337 &mut info as *mut _ as *mut _,
338 size as c::DWORD,
339 )
340 })?;
341 Ok(())
342 }
343
344 #[cfg(not(target_vendor = "uwp"))]
file_attr(&self) -> io::Result<FileAttr>345 pub fn file_attr(&self) -> io::Result<FileAttr> {
346 unsafe {
347 let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
348 cvt(c::GetFileInformationByHandle(
349 self.handle.as_raw_handle(),
350 &mut info,
351 ))?;
352 let mut reparse_tag = 0;
353 if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
354 let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
355 cvt(c::GetFileInformationByHandleEx(
356 self.handle.as_raw_handle(),
357 c::FileAttributeTagInfo,
358 ptr::addr_of_mut!(attr_tag).cast(),
359 mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>()
360 .try_into()
361 .unwrap(),
362 ))?;
363 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
364 reparse_tag = attr_tag.ReparseTag;
365 }
366 }
367 Ok(FileAttr {
368 attributes: info.dwFileAttributes,
369 creation_time: info.ftCreationTime,
370 last_access_time: info.ftLastAccessTime,
371 last_write_time: info.ftLastWriteTime,
372 file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),
373 reparse_tag,
374 volume_serial_number: Some(info.dwVolumeSerialNumber),
375 number_of_links: Some(info.nNumberOfLinks),
376 file_index: Some(
377 (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),
378 ),
379 })
380 }
381 }
382
383 #[cfg(target_vendor = "uwp")]
file_attr(&self) -> io::Result<FileAttr>384 pub fn file_attr(&self) -> io::Result<FileAttr> {
385 unsafe {
386 let mut info: c::FILE_BASIC_INFO = mem::zeroed();
387 let size = mem::size_of_val(&info);
388 cvt(c::GetFileInformationByHandleEx(
389 self.handle.as_raw_handle(),
390 c::FileBasicInfo,
391 &mut info as *mut _ as *mut dlibc::c_void,
392 size as c::DWORD,
393 ))?;
394 let mut attr = FileAttr {
395 attributes: info.FileAttributes,
396 creation_time: c::FILETIME {
397 dwLowDateTime: info.CreationTime as c::DWORD,
398 dwHighDateTime: (info.CreationTime >> 32) as c::DWORD,
399 },
400 last_access_time: c::FILETIME {
401 dwLowDateTime: info.LastAccessTime as c::DWORD,
402 dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD,
403 },
404 last_write_time: c::FILETIME {
405 dwLowDateTime: info.LastWriteTime as c::DWORD,
406 dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD,
407 },
408 file_size: 0,
409 reparse_tag: 0,
410 volume_serial_number: None,
411 number_of_links: None,
412 file_index: None,
413 };
414 let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
415 let size = mem::size_of_val(&info);
416 cvt(c::GetFileInformationByHandleEx(
417 self.handle.as_raw_handle(),
418 c::FileStandardInfo,
419 &mut info as *mut _ as *mut dlibc::c_void,
420 size as c::DWORD,
421 ))?;
422 attr.file_size = info.AllocationSize as u64;
423 attr.number_of_links = Some(info.NumberOfLinks);
424 if attr.file_type().is_reparse_point() {
425 let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
426 cvt(c::GetFileInformationByHandleEx(
427 self.handle.as_raw_handle(),
428 c::FileAttributeTagInfo,
429 ptr::addr_of_mut!(attr_tag).cast(),
430 mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>()
431 .try_into()
432 .unwrap(),
433 ))?;
434 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
435 attr.reparse_tag = attr_tag.ReparseTag;
436 }
437 }
438 Ok(attr)
439 }
440 }
441
read(&self, buf: &mut [u8]) -> io::Result<usize>442 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
443 self.handle.read(buf)
444 }
445
read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>446 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
447 self.handle.read_vectored(bufs)
448 }
449
450 #[inline]
is_read_vectored(&self) -> bool451 pub fn is_read_vectored(&self) -> bool {
452 self.handle.is_read_vectored()
453 }
454
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>455 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
456 self.handle.read_at(buf, offset)
457 }
458
read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()>459 pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
460 self.handle.read_buf(cursor)
461 }
462
write(&self, buf: &[u8]) -> io::Result<usize>463 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
464 self.handle.write(buf)
465 }
466
write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize>467 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
468 self.handle.write_vectored(bufs)
469 }
470
471 #[inline]
is_write_vectored(&self) -> bool472 pub fn is_write_vectored(&self) -> bool {
473 self.handle.is_write_vectored()
474 }
475
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>476 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
477 self.handle.write_at(buf, offset)
478 }
479
flush(&self) -> io::Result<()>480 pub fn flush(&self) -> io::Result<()> {
481 Ok(())
482 }
483
seek(&self, pos: SeekFrom) -> io::Result<u64>484 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
485 let (whence, pos) = match pos {
486 // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
487 // integer as `u64`.
488 SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
489 SeekFrom::End(n) => (c::FILE_END, n),
490 SeekFrom::Current(n) => (c::FILE_CURRENT, n),
491 };
492 let pos = pos as c::LARGE_INTEGER;
493 let mut newpos = 0;
494 cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
495 Ok(newpos as u64)
496 }
497
duplicate(&self) -> io::Result<File>498 pub fn duplicate(&self) -> io::Result<File> {
499 Ok(Self {
500 handle: self.handle.try_clone()?,
501 })
502 }
503
504 // NB: returned pointer is derived from `space`, and has provenance to
505 // match. A raw pointer is returned rather than a reference in order to
506 // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
reparse_point( &self, space: &mut Align8<[MaybeUninit<u8>]>, ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)>507 fn reparse_point(
508 &self,
509 space: &mut Align8<[MaybeUninit<u8>]>,
510 ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
511 unsafe {
512 let mut bytes = 0;
513 cvt({
514 // Grab this in advance to avoid it invalidating the pointer
515 // we get from `space.0.as_mut_ptr()`.
516 let len = space.0.len();
517 c::DeviceIoControl(
518 self.handle.as_raw_handle(),
519 c::FSCTL_GET_REPARSE_POINT,
520 ptr::null_mut(),
521 0,
522 space.0.as_mut_ptr().cast(),
523 len as c::DWORD,
524 &mut bytes,
525 ptr::null_mut(),
526 )
527 })?;
528 const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
529 Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
530 }
531 }
532
readlink(&self) -> io::Result<PathBuf>533 fn readlink(&self) -> io::Result<PathBuf> {
534 let mut space =
535 Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
536 let (_bytes, buf) = self.reparse_point(&mut space)?;
537 unsafe {
538 let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
539 c::IO_REPARSE_TAG_SYMLINK => {
540 let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
541 ptr::addr_of_mut!((*buf).rest).cast();
542 assert!(info.is_aligned());
543 (
544 ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
545 (*info).SubstituteNameOffset / 2,
546 (*info).SubstituteNameLength / 2,
547 (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
548 )
549 }
550 c::IO_REPARSE_TAG_MOUNT_POINT => {
551 let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
552 ptr::addr_of_mut!((*buf).rest).cast();
553 assert!(info.is_aligned());
554 (
555 ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
556 (*info).SubstituteNameOffset / 2,
557 (*info).SubstituteNameLength / 2,
558 false,
559 )
560 }
561 _ => {
562 return Err(io::const_io_error!(
563 io::ErrorKind::Uncategorized,
564 "Unsupported reparse point type",
565 ));
566 }
567 };
568 let subst_ptr = path_buffer.add(subst_off.into());
569 let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
570 // Absolute paths start with an NT internal namespace prefix `\??\`
571 // We should not let it leak through.
572 if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
573 // Turn `\??\` into `\\?\` (a verbatim path).
574 subst[1] = b'\\' as u16;
575 // Attempt to convert to a more user-friendly path.
576 let user = super::args::from_wide_to_user_path(
577 subst.iter().copied().chain([0]).collect(),
578 )?;
579 Ok(PathBuf::from(OsString::from_wide(
580 &user.strip_suffix(&[0]).unwrap_or(&user),
581 )))
582 } else {
583 Ok(PathBuf::from(OsString::from_wide(subst)))
584 }
585 }
586 }
587
set_permissions(&self, perm: FilePermissions) -> io::Result<()>588 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
589 let mut info = c::FILE_BASIC_INFO {
590 CreationTime: 0,
591 LastAccessTime: 0,
592 LastWriteTime: 0,
593 ChangeTime: 0,
594 FileAttributes: perm.attrs,
595 };
596 let size = mem::size_of_val(&info);
597 cvt(unsafe {
598 c::SetFileInformationByHandle(
599 self.handle.as_raw_handle(),
600 c::FileBasicInfo,
601 &mut info as *mut _ as *mut _,
602 size as c::DWORD,
603 )
604 })?;
605 Ok(())
606 }
607
set_times(&self, times: FileTimes) -> io::Result<()>608 pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
609 let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
610 if times.accessed.map_or(false, is_zero)
611 || times.modified.map_or(false, is_zero)
612 || times.created.map_or(false, is_zero)
613 {
614 return Err(io::const_io_error!(
615 io::ErrorKind::InvalidInput,
616 "Cannot set file timestamp to 0",
617 ));
618 }
619 let is_max =
620 |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX;
621 if times.accessed.map_or(false, is_max)
622 || times.modified.map_or(false, is_max)
623 || times.created.map_or(false, is_max)
624 {
625 return Err(io::const_io_error!(
626 io::ErrorKind::InvalidInput,
627 "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
628 ));
629 }
630 cvt(unsafe {
631 let created = times
632 .created
633 .as_ref()
634 .map(|a| a as *const c::FILETIME)
635 .unwrap_or(ptr::null());
636 let accessed = times
637 .accessed
638 .as_ref()
639 .map(|a| a as *const c::FILETIME)
640 .unwrap_or(ptr::null());
641 let modified = times
642 .modified
643 .as_ref()
644 .map(|a| a as *const c::FILETIME)
645 .unwrap_or(ptr::null());
646 c::SetFileTime(self.as_raw_handle(), created, accessed, modified)
647 })?;
648 Ok(())
649 }
650
651 /// Get only basic file information such as attributes and file times.
basic_info(&self) -> io::Result<c::FILE_BASIC_INFO>652 fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
653 unsafe {
654 let mut info: c::FILE_BASIC_INFO = mem::zeroed();
655 let size = mem::size_of_val(&info);
656 cvt(c::GetFileInformationByHandleEx(
657 self.handle.as_raw_handle(),
658 c::FileBasicInfo,
659 &mut info as *mut _ as *mut dlibc::c_void,
660 size as c::DWORD,
661 ))?;
662 Ok(info)
663 }
664 }
665 /// Delete using POSIX semantics.
666 ///
667 /// Files will be deleted as soon as the handle is closed. This is supported
668 /// for Windows 10 1607 (aka RS1) and later. However some filesystem
669 /// drivers will not support it even then, e.g. FAT32.
670 ///
671 /// If the operation is not supported for this filesystem or OS version
672 /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
posix_delete(&self) -> io::Result<()>673 fn posix_delete(&self) -> io::Result<()> {
674 let mut info = c::FILE_DISPOSITION_INFO_EX {
675 Flags: c::FILE_DISPOSITION_FLAG_DELETE
676 | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
677 | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
678 };
679 let size = mem::size_of_val(&info);
680 cvt(unsafe {
681 c::SetFileInformationByHandle(
682 self.handle.as_raw_handle(),
683 c::FileDispositionInfoEx,
684 &mut info as *mut _ as *mut _,
685 size as c::DWORD,
686 )
687 })?;
688 Ok(())
689 }
690
691 /// Delete a file using win32 semantics. The file won't actually be deleted
692 /// until all file handles are closed. However, marking a file for deletion
693 /// will prevent anyone from opening a new handle to the file.
win32_delete(&self) -> io::Result<()>694 fn win32_delete(&self) -> io::Result<()> {
695 let mut info = c::FILE_DISPOSITION_INFO {
696 DeleteFile: c::TRUE as _,
697 };
698 let size = mem::size_of_val(&info);
699 cvt(unsafe {
700 c::SetFileInformationByHandle(
701 self.handle.as_raw_handle(),
702 c::FileDispositionInfo,
703 &mut info as *mut _ as *mut _,
704 size as c::DWORD,
705 )
706 })?;
707 Ok(())
708 }
709
710 /// Fill the given buffer with as many directory entries as will fit.
711 /// This will remember its position and continue from the last call unless
712 /// `restart` is set to `true`.
713 ///
714 /// The returned bool indicates if there are more entries or not.
715 /// It is an error if `self` is not a directory.
716 ///
717 /// # Symlinks and other reparse points
718 ///
719 /// On Windows a file is either a directory or a non-directory.
720 /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
721 /// So if you open a link (not its target) and iterate the directory,
722 /// you will always iterate an empty directory regardless of the target.
fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool>723 fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool> {
724 let class = if restart {
725 c::FileIdBothDirectoryRestartInfo
726 } else {
727 c::FileIdBothDirectoryInfo
728 };
729
730 unsafe {
731 let result = cvt(c::GetFileInformationByHandleEx(
732 self.handle.as_raw_handle(),
733 class,
734 buffer.as_mut_ptr().cast(),
735 buffer.capacity() as _,
736 ));
737 match result {
738 Ok(_) => Ok(true),
739 Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false),
740 Err(e) => Err(e),
741 }
742 }
743 }
744 }
745
746 /// A buffer for holding directory entries.
747 struct DirBuff {
748 buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
749 }
750 impl DirBuff {
751 const BUFFER_SIZE: usize = 1024;
new() -> Self752 fn new() -> Self {
753 Self {
754 // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
755 // initialization.
756 buffer: unsafe { Box::new_uninit().assume_init() },
757 }
758 }
capacity(&self) -> usize759 fn capacity(&self) -> usize {
760 self.buffer.0.len()
761 }
as_mut_ptr(&mut self) -> *mut u8762 fn as_mut_ptr(&mut self) -> *mut u8 {
763 self.buffer.0.as_mut_ptr().cast()
764 }
765 /// Returns a `DirBuffIter`.
iter(&self) -> DirBuffIter<'_>766 fn iter(&self) -> DirBuffIter<'_> {
767 DirBuffIter::new(self)
768 }
769 }
770 impl AsRef<[MaybeUninit<u8>]> for DirBuff {
as_ref(&self) -> &[MaybeUninit<u8>]771 fn as_ref(&self) -> &[MaybeUninit<u8>] {
772 &self.buffer.0
773 }
774 }
775
776 /// An iterator over entries stored in a `DirBuff`.
777 ///
778 /// Currently only returns file names (UTF-16 encoded).
779 struct DirBuffIter<'a> {
780 buffer: Option<&'a [MaybeUninit<u8>]>,
781 cursor: usize,
782 }
783 impl<'a> DirBuffIter<'a> {
new(buffer: &'a DirBuff) -> Self784 fn new(buffer: &'a DirBuff) -> Self {
785 Self {
786 buffer: Some(buffer.as_ref()),
787 cursor: 0,
788 }
789 }
790 }
791 impl<'a> Iterator for DirBuffIter<'a> {
792 type Item = (Cow<'a, [u16]>, bool);
next(&mut self) -> Option<Self::Item>793 fn next(&mut self) -> Option<Self::Item> {
794 use crate::std::mem::size_of;
795 let buffer = &self.buffer?[self.cursor..];
796
797 // Get the name and next entry from the buffer.
798 // SAFETY:
799 // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
800 // field (the file name) is unsized. So an offset has to be used to
801 // get the file name slice.
802 // - The OS has guaranteed initialization of the fields of
803 // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
804 // `FileNameLength` bytes)
805 let (name, is_directory, next_entry) = unsafe {
806 let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
807 // While this is guaranteed to be aligned in documentation for
808 // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
809 // it does not seem that reality is so kind, and assuming this
810 // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
811 // presumably, this can be blamed on buggy filesystem drivers, but who knows.
812 let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize;
813 let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize;
814 let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned();
815 let name = from_maybe_unaligned(
816 ptr::addr_of!((*info).FileName).cast::<u16>(),
817 length / size_of::<u16>(),
818 );
819 let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
820
821 (name, is_directory, next_entry)
822 };
823
824 if next_entry == 0 {
825 self.buffer = None
826 } else {
827 self.cursor += next_entry
828 }
829
830 // Skip `.` and `..` pseudo entries.
831 const DOT: u16 = b'.' as u16;
832 match &name[..] {
833 [DOT] | [DOT, DOT] => self.next(),
834 _ => Some((name, is_directory)),
835 }
836 }
837 }
838
from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]>839 unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
840 if p.is_aligned() {
841 Cow::Borrowed(crate::std::slice::from_raw_parts(p, len))
842 } else {
843 Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
844 }
845 }
846
847 /// Open a link relative to the parent directory, ensure no symlinks are followed.
open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File>848 fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
849 // This is implemented using the lower level `NtCreateFile` function as
850 // unfortunately opening a file relative to a parent is not supported by
851 // win32 functions. It is however a fundamental feature of the NT kernel.
852 //
853 // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
854 unsafe {
855 let mut handle = ptr::null_mut();
856 let mut io_status = c::IO_STATUS_BLOCK::PENDING;
857 let mut name_str = c::UNICODE_STRING::from_ref(name);
858 use crate::std::sync::atomic::{AtomicU32, Ordering};
859 // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
860 // tricked into following a symlink. However, it may not be available in
861 // earlier versions of Windows.
862 static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
863 let mut object = c::OBJECT_ATTRIBUTES {
864 ObjectName: &mut name_str,
865 RootDirectory: parent.as_raw_handle(),
866 Attributes: ATTRIBUTES.load(Ordering::Relaxed),
867 ..c::OBJECT_ATTRIBUTES::default()
868 };
869 let status = c::NtCreateFile(
870 &mut handle,
871 access,
872 &mut object,
873 &mut io_status,
874 crate::std::ptr::null_mut(),
875 0,
876 c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
877 c::FILE_OPEN,
878 // If `name` is a symlink then open the link rather than the target.
879 c::FILE_OPEN_REPARSE_POINT,
880 crate::std::ptr::null_mut(),
881 0,
882 );
883 // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
884 if c::nt_success(status) {
885 Ok(File::from_raw_handle(handle))
886 } else if status == c::STATUS_DELETE_PENDING {
887 // We make a special exception for `STATUS_DELETE_PENDING` because
888 // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
889 // very unhelpful.
890 Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as _))
891 } else if status == c::STATUS_INVALID_PARAMETER
892 && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
893 {
894 // Try without `OBJ_DONT_REPARSE`. See above.
895 ATTRIBUTES.store(0, Ordering::Relaxed);
896 open_link_no_reparse(parent, name, access)
897 } else {
898 Err(io::Error::from_raw_os_error(
899 c::RtlNtStatusToDosError(status) as _,
900 ))
901 }
902 }
903 }
904
905 impl AsInner<Handle> for File {
906 #[inline]
as_inner(&self) -> &Handle907 fn as_inner(&self) -> &Handle {
908 &self.handle
909 }
910 }
911
912 impl IntoInner<Handle> for File {
into_inner(self) -> Handle913 fn into_inner(self) -> Handle {
914 self.handle
915 }
916 }
917
918 impl FromInner<Handle> for File {
from_inner(handle: Handle) -> File919 fn from_inner(handle: Handle) -> File {
920 File { handle }
921 }
922 }
923
924 impl AsHandle for File {
as_handle(&self) -> BorrowedHandle<'_>925 fn as_handle(&self) -> BorrowedHandle<'_> {
926 self.as_inner().as_handle()
927 }
928 }
929
930 impl AsRawHandle for File {
as_raw_handle(&self) -> RawHandle931 fn as_raw_handle(&self) -> RawHandle {
932 self.as_inner().as_raw_handle()
933 }
934 }
935
936 impl IntoRawHandle for File {
into_raw_handle(self) -> RawHandle937 fn into_raw_handle(self) -> RawHandle {
938 self.into_inner().into_raw_handle()
939 }
940 }
941
942 impl FromRawHandle for File {
from_raw_handle(raw_handle: RawHandle) -> Self943 unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
944 Self {
945 handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)),
946 }
947 }
948 }
949
950 impl fmt::Debug for File {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result951 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
952 // FIXME(#24570): add more info here (e.g., mode)
953 let mut b = f.debug_struct("File");
954 b.field("handle", &self.handle.as_raw_handle());
955 if let Ok(path) = get_path(&self) {
956 b.field("path", &path);
957 }
958 b.finish()
959 }
960 }
961
962 impl FileAttr {
size(&self) -> u64963 pub fn size(&self) -> u64 {
964 self.file_size
965 }
966
perm(&self) -> FilePermissions967 pub fn perm(&self) -> FilePermissions {
968 FilePermissions {
969 attrs: self.attributes,
970 }
971 }
972
attrs(&self) -> u32973 pub fn attrs(&self) -> u32 {
974 self.attributes
975 }
976
file_type(&self) -> FileType977 pub fn file_type(&self) -> FileType {
978 FileType::new(self.attributes, self.reparse_tag)
979 }
980
modified(&self) -> io::Result<SystemTime>981 pub fn modified(&self) -> io::Result<SystemTime> {
982 Ok(SystemTime::from(self.last_write_time))
983 }
984
accessed(&self) -> io::Result<SystemTime>985 pub fn accessed(&self) -> io::Result<SystemTime> {
986 Ok(SystemTime::from(self.last_access_time))
987 }
988
created(&self) -> io::Result<SystemTime>989 pub fn created(&self) -> io::Result<SystemTime> {
990 Ok(SystemTime::from(self.creation_time))
991 }
992
modified_u64(&self) -> u64993 pub fn modified_u64(&self) -> u64 {
994 to_u64(&self.last_write_time)
995 }
996
accessed_u64(&self) -> u64997 pub fn accessed_u64(&self) -> u64 {
998 to_u64(&self.last_access_time)
999 }
1000
created_u64(&self) -> u641001 pub fn created_u64(&self) -> u64 {
1002 to_u64(&self.creation_time)
1003 }
1004
volume_serial_number(&self) -> Option<u32>1005 pub fn volume_serial_number(&self) -> Option<u32> {
1006 self.volume_serial_number
1007 }
1008
number_of_links(&self) -> Option<u32>1009 pub fn number_of_links(&self) -> Option<u32> {
1010 self.number_of_links
1011 }
1012
file_index(&self) -> Option<u64>1013 pub fn file_index(&self) -> Option<u64> {
1014 self.file_index
1015 }
1016 }
1017 impl From<c::WIN32_FIND_DATAW> for FileAttr {
from(wfd: c::WIN32_FIND_DATAW) -> Self1018 fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
1019 FileAttr {
1020 attributes: wfd.dwFileAttributes,
1021 creation_time: wfd.ftCreationTime,
1022 last_access_time: wfd.ftLastAccessTime,
1023 last_write_time: wfd.ftLastWriteTime,
1024 file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
1025 reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
1026 // reserved unless this is a reparse point
1027 wfd.dwReserved0
1028 } else {
1029 0
1030 },
1031 volume_serial_number: None,
1032 number_of_links: None,
1033 file_index: None,
1034 }
1035 }
1036 }
1037
to_u64(ft: &c::FILETIME) -> u641038 fn to_u64(ft: &c::FILETIME) -> u64 {
1039 (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
1040 }
1041
1042 impl FilePermissions {
readonly(&self) -> bool1043 pub fn readonly(&self) -> bool {
1044 self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
1045 }
1046
set_readonly(&mut self, readonly: bool)1047 pub fn set_readonly(&mut self, readonly: bool) {
1048 if readonly {
1049 self.attrs |= c::FILE_ATTRIBUTE_READONLY;
1050 } else {
1051 self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
1052 }
1053 }
1054 }
1055
1056 impl FileTimes {
set_accessed(&mut self, t: SystemTime)1057 pub fn set_accessed(&mut self, t: SystemTime) {
1058 self.accessed = Some(t.into_inner());
1059 }
1060
set_modified(&mut self, t: SystemTime)1061 pub fn set_modified(&mut self, t: SystemTime) {
1062 self.modified = Some(t.into_inner());
1063 }
1064
set_created(&mut self, t: SystemTime)1065 pub fn set_created(&mut self, t: SystemTime) {
1066 self.created = Some(t.into_inner());
1067 }
1068 }
1069
1070 impl FileType {
new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType1071 fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
1072 FileType {
1073 attributes: attrs,
1074 reparse_tag,
1075 }
1076 }
is_dir(&self) -> bool1077 pub fn is_dir(&self) -> bool {
1078 !self.is_symlink() && self.is_directory()
1079 }
is_file(&self) -> bool1080 pub fn is_file(&self) -> bool {
1081 !self.is_symlink() && !self.is_directory()
1082 }
is_symlink(&self) -> bool1083 pub fn is_symlink(&self) -> bool {
1084 self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
1085 }
is_symlink_dir(&self) -> bool1086 pub fn is_symlink_dir(&self) -> bool {
1087 self.is_symlink() && self.is_directory()
1088 }
is_symlink_file(&self) -> bool1089 pub fn is_symlink_file(&self) -> bool {
1090 self.is_symlink() && !self.is_directory()
1091 }
is_directory(&self) -> bool1092 fn is_directory(&self) -> bool {
1093 self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
1094 }
is_reparse_point(&self) -> bool1095 fn is_reparse_point(&self) -> bool {
1096 self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
1097 }
is_reparse_tag_name_surrogate(&self) -> bool1098 fn is_reparse_tag_name_surrogate(&self) -> bool {
1099 self.reparse_tag & 0x20000000 != 0
1100 }
1101 }
1102
1103 impl DirBuilder {
new() -> DirBuilder1104 pub fn new() -> DirBuilder {
1105 DirBuilder
1106 }
1107
mkdir(&self, p: &Path) -> io::Result<()>1108 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1109 let p = maybe_verbatim(p)?;
1110 cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
1111 Ok(())
1112 }
1113 }
1114
readdir(p: &Path) -> io::Result<ReadDir>1115 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1116 let root = p.to_path_buf();
1117 let star = p.join("*");
1118 let path = maybe_verbatim(&star)?;
1119
1120 unsafe {
1121 let mut wfd = mem::zeroed();
1122 let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
1123 if find_handle != c::INVALID_HANDLE_VALUE {
1124 Ok(ReadDir {
1125 handle: FindNextFileHandle(find_handle),
1126 root: Arc::new(root),
1127 first: Some(wfd),
1128 })
1129 } else {
1130 Err(Error::last_os_error())
1131 }
1132 }
1133 }
1134
unlink(p: &Path) -> io::Result<()>1135 pub fn unlink(p: &Path) -> io::Result<()> {
1136 let p_u16s = maybe_verbatim(p)?;
1137 cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
1138 Ok(())
1139 }
1140
rename(old: &Path, new: &Path) -> io::Result<()>1141 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1142 let old = maybe_verbatim(old)?;
1143 let new = maybe_verbatim(new)?;
1144 cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?;
1145 Ok(())
1146 }
1147
rmdir(p: &Path) -> io::Result<()>1148 pub fn rmdir(p: &Path) -> io::Result<()> {
1149 let p = maybe_verbatim(p)?;
1150 cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
1151 Ok(())
1152 }
1153
1154 /// Open a file or directory without following symlinks.
open_link(path: &Path, access_mode: u32) -> io::Result<File>1155 fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
1156 let mut opts = OpenOptions::new();
1157 opts.access_mode(access_mode);
1158 // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
1159 // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
1160 opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
1161 File::open(path, &opts)
1162 }
1163
remove_dir_all(path: &Path) -> io::Result<()>1164 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
1165 let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
1166
1167 // Test if the file is not a directory or a symlink to a directory.
1168 if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
1169 return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
1170 }
1171
1172 match remove_dir_all_iterative(&file, File::posix_delete) {
1173 Err(e) => {
1174 if let Some(code) = e.raw_os_error() {
1175 match code as u32 {
1176 // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1177 c::ERROR_NOT_SUPPORTED
1178 | c::ERROR_INVALID_FUNCTION
1179 | c::ERROR_INVALID_PARAMETER => {
1180 remove_dir_all_iterative(&file, File::win32_delete)
1181 }
1182 _ => Err(e),
1183 }
1184 } else {
1185 Err(e)
1186 }
1187 }
1188 ok => ok,
1189 }
1190 }
1191
remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()>1192 fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> {
1193 // When deleting files we may loop this many times when certain error conditions occur.
1194 // This allows remove_dir_all to succeed when the error is temporary.
1195 const MAX_RETRIES: u32 = 10;
1196
1197 let mut buffer = DirBuff::new();
1198 let mut dirlist = vec![f.duplicate()?];
1199
1200 // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
1201 fn copy_handle(f: &File) -> mem::ManuallyDrop<File> {
1202 unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) }
1203 }
1204
1205 let mut restart = true;
1206 while let Some(dir) = dirlist.last() {
1207 let dir = copy_handle(dir);
1208
1209 // Fill the buffer and iterate the entries.
1210 let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
1211 restart = false;
1212 for (name, is_directory) in buffer.iter() {
1213 if is_directory {
1214 let child_dir = open_link_no_reparse(
1215 &dir,
1216 &name,
1217 c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
1218 );
1219 // On success, add the handle to the queue.
1220 // If opening the directory fails we treat it the same as a file
1221 if let Ok(child_dir) = child_dir {
1222 dirlist.push(child_dir);
1223 continue;
1224 }
1225 }
1226 for i in 1..=MAX_RETRIES {
1227 let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
1228 match result {
1229 Ok(f) => delete(&f)?,
1230 // Already deleted, so skip.
1231 Err(e) if e.kind() == io::ErrorKind::NotFound => break,
1232 // Retry a few times if the file is locked or a delete is already in progress.
1233 Err(e)
1234 if i < MAX_RETRIES
1235 && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
1236 || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
1237 // Otherwise return the error.
1238 Err(e) => return Err(e),
1239 }
1240 thread::yield_now();
1241 }
1242 }
1243 // If there were no more files then delete the directory.
1244 if !more_data {
1245 if let Some(dir) = dirlist.pop() {
1246 // Retry deleting a few times in case we need to wait for a file to be deleted.
1247 for i in 1..=MAX_RETRIES {
1248 let result = delete(&dir);
1249 if let Err(e) = result {
1250 if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty {
1251 return Err(e);
1252 }
1253 thread::yield_now();
1254 } else {
1255 break;
1256 }
1257 }
1258 }
1259 }
1260 }
1261 Ok(())
1262 }
1263
readlink(path: &Path) -> io::Result<PathBuf>1264 pub fn readlink(path: &Path) -> io::Result<PathBuf> {
1265 // Open the link with no access mode, instead of generic read.
1266 // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
1267 // this is needed for a common case.
1268 let mut opts = OpenOptions::new();
1269 opts.access_mode(0);
1270 opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1271 let file = File::open(&path, &opts)?;
1272 file.readlink()
1273 }
1274
symlink(original: &Path, link: &Path) -> io::Result<()>1275 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1276 symlink_inner(original, link, false)
1277 }
1278
symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()>1279 pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
1280 let original = to_u16s(original)?;
1281 let link = maybe_verbatim(link)?;
1282 let flags = if dir {
1283 c::SYMBOLIC_LINK_FLAG_DIRECTORY
1284 } else {
1285 0
1286 };
1287 // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
1288 // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
1289 // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
1290 // added to dwFlags to opt into this behaviour.
1291 let result = cvt(unsafe {
1292 c::CreateSymbolicLinkW(
1293 link.as_ptr(),
1294 original.as_ptr(),
1295 flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1296 ) as c::BOOL
1297 });
1298 if let Err(err) = result {
1299 if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
1300 // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1301 // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
1302 cvt(unsafe {
1303 c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL
1304 })?;
1305 } else {
1306 return Err(err);
1307 }
1308 }
1309 Ok(())
1310 }
1311
1312 #[cfg(not(target_vendor = "uwp"))]
link(original: &Path, link: &Path) -> io::Result<()>1313 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1314 let original = maybe_verbatim(original)?;
1315 let link = maybe_verbatim(link)?;
1316 cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
1317 Ok(())
1318 }
1319
1320 #[cfg(target_vendor = "uwp")]
link(_original: &Path, _link: &Path) -> io::Result<()>1321 pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
1322 return Err(io::const_io_error!(
1323 io::ErrorKind::Unsupported,
1324 "hard link are not supported on UWP",
1325 ));
1326 }
1327
stat(path: &Path) -> io::Result<FileAttr>1328 pub fn stat(path: &Path) -> io::Result<FileAttr> {
1329 match metadata(path, ReparsePoint::Follow) {
1330 Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
1331 if let Ok(attrs) = lstat(path) {
1332 if !attrs.file_type().is_symlink() {
1333 return Ok(attrs);
1334 }
1335 }
1336 Err(err)
1337 }
1338 result => result,
1339 }
1340 }
1341
lstat(path: &Path) -> io::Result<FileAttr>1342 pub fn lstat(path: &Path) -> io::Result<FileAttr> {
1343 metadata(path, ReparsePoint::Open)
1344 }
1345
1346 #[repr(u32)]
1347 #[derive(Clone, Copy, PartialEq, Eq)]
1348 enum ReparsePoint {
1349 Follow = 0,
1350 Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
1351 }
1352 impl ReparsePoint {
as_flag(self) -> u321353 fn as_flag(self) -> u32 {
1354 self as u32
1355 }
1356 }
1357
metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr>1358 fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
1359 let mut opts = OpenOptions::new();
1360 // No read or write permissions are necessary
1361 opts.access_mode(0);
1362 opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
1363
1364 // Attempt to open the file normally.
1365 // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
1366 // If the fallback fails for any reason we return the original error.
1367 match File::open(path, &opts) {
1368 Ok(file) => file.file_attr(),
1369 Err(e)
1370 if [
1371 Some(c::ERROR_SHARING_VIOLATION as _),
1372 Some(c::ERROR_ACCESS_DENIED as _),
1373 ]
1374 .contains(&e.raw_os_error()) =>
1375 {
1376 // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource.
1377 // One such example is `System Volume Information` as default but can be created as well
1378 // `ERROR_SHARING_VIOLATION` will almost never be returned.
1379 // Usually if a file is locked you can still read some metadata.
1380 // However, there are special system files, such as
1381 // `C:\hiberfil.sys`, that are locked in a way that denies even that.
1382 unsafe {
1383 let path = maybe_verbatim(path)?;
1384
1385 // `FindFirstFileW` accepts wildcard file names.
1386 // Fortunately wildcards are not valid file names and
1387 // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
1388 // therefore it's safe to assume the file name given does not
1389 // include wildcards.
1390 let mut wfd = mem::zeroed();
1391 let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
1392
1393 if handle == c::INVALID_HANDLE_VALUE {
1394 // This can fail if the user does not have read access to the
1395 // directory.
1396 Err(e)
1397 } else {
1398 // We no longer need the find handle.
1399 c::FindClose(handle);
1400
1401 // `FindFirstFileW` reads the cached file information from the
1402 // directory. The downside is that this metadata may be outdated.
1403 let attrs = FileAttr::from(wfd);
1404 if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
1405 Err(e)
1406 } else {
1407 Ok(attrs)
1408 }
1409 }
1410 }
1411 }
1412 Err(e) => Err(e),
1413 }
1414 }
1415
set_perm(p: &Path, perm: FilePermissions) -> io::Result<()>1416 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1417 let p = maybe_verbatim(p)?;
1418 unsafe {
1419 cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
1420 Ok(())
1421 }
1422 }
1423
get_path(f: &File) -> io::Result<PathBuf>1424 fn get_path(f: &File) -> io::Result<PathBuf> {
1425 super::fill_utf16_buf(
1426 |buf, sz| unsafe {
1427 c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1428 },
1429 |buf| PathBuf::from(OsString::from_wide(buf)),
1430 )
1431 }
1432
canonicalize(p: &Path) -> io::Result<PathBuf>1433 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1434 let mut opts = OpenOptions::new();
1435 // No read or write permissions are necessary
1436 opts.access_mode(0);
1437 // This flag is so we can open directories too
1438 opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1439 let f = File::open(p, &opts)?;
1440 get_path(&f)
1441 }
1442
copy(from: &Path, to: &Path) -> io::Result<u64>1443 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1444 unsafe extern "system" fn callback(
1445 _TotalFileSize: c::LARGE_INTEGER,
1446 _TotalBytesTransferred: c::LARGE_INTEGER,
1447 _StreamSize: c::LARGE_INTEGER,
1448 StreamBytesTransferred: c::LARGE_INTEGER,
1449 dwStreamNumber: c::DWORD,
1450 _dwCallbackReason: c::DWORD,
1451 _hSourceFile: c::HANDLE,
1452 _hDestinationFile: c::HANDLE,
1453 lpData: c::LPCVOID,
1454 ) -> c::DWORD {
1455 if dwStreamNumber == 1 {
1456 *(lpData as *mut i64) = StreamBytesTransferred;
1457 }
1458 c::PROGRESS_CONTINUE
1459 }
1460 let pfrom = maybe_verbatim(from)?;
1461 let pto = maybe_verbatim(to)?;
1462 let mut size = 0i64;
1463 cvt(unsafe {
1464 c::CopyFileExW(
1465 pfrom.as_ptr(),
1466 pto.as_ptr(),
1467 Some(callback),
1468 &mut size as *mut _ as *mut _,
1469 ptr::null_mut(),
1470 0,
1471 )
1472 })?;
1473 Ok(size as u64)
1474 }
1475
1476 #[allow(dead_code)]
symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>( original: P, junction: Q, ) -> io::Result<()>1477 pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(
1478 original: P,
1479 junction: Q,
1480 ) -> io::Result<()> {
1481 symlink_junction_inner(original.as_ref(), junction.as_ref())
1482 }
1483
1484 // Creating a directory junction on windows involves dealing with reparse
1485 // points and the DeviceIoControl function, and this code is a skeleton of
1486 // what can be found here:
1487 //
1488 // http://www.flexhex.com/docs/articles/hard-links.phtml
1489 #[allow(dead_code)]
symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()>1490 fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
1491 let d = DirBuilder::new();
1492 d.mkdir(&junction)?;
1493
1494 let mut opts = OpenOptions::new();
1495 opts.write(true);
1496 opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1497 let f = File::open(junction, &opts)?;
1498 let h = f.as_inner().as_raw_handle();
1499 unsafe {
1500 let mut data =
1501 Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
1502 let data_ptr = data.0.as_mut_ptr();
1503 let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
1504 let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
1505 // Zero the header to ensure it's fully initialized, including reserved parameters.
1506 *db = mem::zeroed();
1507 let reparse_target_slice = {
1508 let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
1509 // Compute offset in bytes and then divide so that we round down
1510 // rather than hit any UB (admittedly this arithmetic should work
1511 // out so that this isn't necessary)
1512 let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap();
1513 let buf_len_wchars = buf_len_bytes / core::mem::size_of::<c::WCHAR>();
1514 core::slice::from_raw_parts_mut(buf_start, buf_len_wchars)
1515 };
1516
1517 // FIXME: this conversion is very hacky
1518 let iter = br"\??\"
1519 .iter()
1520 .map(|x| *x as u16)
1521 .chain(original.as_os_str().encode_wide())
1522 .chain(core::iter::once(0));
1523 let mut i = 0;
1524 for c in iter {
1525 if i >= reparse_target_slice.len() {
1526 return Err(crate::std::io::const_io_error!(
1527 crate::std::io::ErrorKind::InvalidFilename,
1528 "Input filename is too long"
1529 ));
1530 }
1531 reparse_target_slice[i] = c;
1532 i += 1;
1533 }
1534 (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
1535 (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
1536 (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
1537 (*db).ReparseDataLength = (*db).ReparseTargetLength as c::DWORD + 12;
1538
1539 let mut ret = 0;
1540 cvt(c::DeviceIoControl(
1541 h as *mut _,
1542 c::FSCTL_SET_REPARSE_POINT,
1543 data_ptr.cast(),
1544 (*db).ReparseDataLength + 8,
1545 ptr::null_mut(),
1546 0,
1547 &mut ret,
1548 ptr::null_mut(),
1549 ))
1550 .map(drop)
1551 }
1552 }
1553
1554 // Try to see if a file exists but, unlike `exists`, report I/O errors.
1555 pub fn try_exists(path: &Path) -> io::Result<bool> {
1556 // Open the file to ensure any symlinks are followed to their target.
1557 let mut opts = OpenOptions::new();
1558 // No read, write, etc access rights are needed.
1559 opts.access_mode(0);
1560 // Backup semantics enables opening directories as well as files.
1561 opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1562 match File::open(path, &opts) {
1563 Err(e) => match e.kind() {
1564 // The file definitely does not exist
1565 io::ErrorKind::NotFound => Ok(false),
1566
1567 // `ERROR_SHARING_VIOLATION` means that the file has been locked by
1568 // another process. This is often temporary so we simply report it
1569 // as the file existing.
1570 _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
1571
1572 // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
1573 // file exists. However, these types of errors are usually more
1574 // permanent so we report them here.
1575 _ => Err(e),
1576 },
1577 // The file was opened successfully therefore it must exist,
1578 Ok(_) => Ok(true),
1579 }
1580 }
1581