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 { 96 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 { 106 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>; 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 { 140 fn drop(&mut self) { 141 let r = unsafe { c::FindClose(self.0) }; 142 debug_assert!(r != 0); 143 } 144 } 145 146 impl 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 160 pub fn path(&self) -> PathBuf { 161 self.root.join(&self.file_name()) 162 } 163 164 pub fn file_name(&self) -> OsString { 165 let filename = super::truncate_utf16_at_nul(&self.data.cFileName); 166 OsString::from_wide(filename) 167 } 168 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 176 pub fn metadata(&self) -> io::Result<FileAttr> { 177 Ok(self.data.into()) 178 } 179 } 180 181 impl OpenOptions { 182 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 201 pub fn read(&mut self, read: bool) { 202 self.read = read; 203 } 204 pub fn write(&mut self, write: bool) { 205 self.write = write; 206 } 207 pub fn append(&mut self, append: bool) { 208 self.append = append; 209 } 210 pub fn truncate(&mut self, truncate: bool) { 211 self.truncate = truncate; 212 } 213 pub fn create(&mut self, create: bool) { 214 self.create = create; 215 } 216 pub fn create_new(&mut self, create_new: bool) { 217 self.create_new = create_new; 218 } 219 220 pub fn custom_flags(&mut self, flags: u32) { 221 self.custom_flags = flags; 222 } 223 pub fn access_mode(&mut self, access_mode: u32) { 224 self.access_mode = Some(access_mode); 225 } 226 pub fn share_mode(&mut self, share_mode: u32) { 227 self.share_mode = share_mode; 228 } 229 pub fn attributes(&mut self, attrs: u32) { 230 self.attributes = attrs; 231 } 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 } 237 pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) { 238 self.security_attributes = attrs; 239 } 240 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 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 283 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 { 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 319 pub fn fsync(&self) -> io::Result<()> { 320 cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; 321 Ok(()) 322 } 323 324 pub fn datasync(&self) -> io::Result<()> { 325 self.fsync() 326 } 327 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"))] 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")] 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 442 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { 443 self.handle.read(buf) 444 } 445 446 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { 447 self.handle.read_vectored(bufs) 448 } 449 450 #[inline] 451 pub fn is_read_vectored(&self) -> bool { 452 self.handle.is_read_vectored() 453 } 454 455 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { 456 self.handle.read_at(buf, offset) 457 } 458 459 pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { 460 self.handle.read_buf(cursor) 461 } 462 463 pub fn write(&self, buf: &[u8]) -> io::Result<usize> { 464 self.handle.write(buf) 465 } 466 467 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { 468 self.handle.write_vectored(bufs) 469 } 470 471 #[inline] 472 pub fn is_write_vectored(&self) -> bool { 473 self.handle.is_write_vectored() 474 } 475 476 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { 477 self.handle.write_at(buf, offset) 478 } 479 480 pub fn flush(&self) -> io::Result<()> { 481 Ok(()) 482 } 483 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 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`. 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 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 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 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. 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`. 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. 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. 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; 752 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 } 759 fn capacity(&self) -> usize { 760 self.buffer.0.len() 761 } 762 fn as_mut_ptr(&mut self) -> *mut u8 { 763 self.buffer.0.as_mut_ptr().cast() 764 } 765 /// Returns a `DirBuffIter`. 766 fn iter(&self) -> DirBuffIter<'_> { 767 DirBuffIter::new(self) 768 } 769 } 770 impl AsRef<[MaybeUninit<u8>]> for DirBuff { 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> { 784 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); 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 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. 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] 907 fn as_inner(&self) -> &Handle { 908 &self.handle 909 } 910 } 911 912 impl IntoInner<Handle> for File { 913 fn into_inner(self) -> Handle { 914 self.handle 915 } 916 } 917 918 impl FromInner<Handle> for File { 919 fn from_inner(handle: Handle) -> File { 920 File { handle } 921 } 922 } 923 924 impl AsHandle for File { 925 fn as_handle(&self) -> BorrowedHandle<'_> { 926 self.as_inner().as_handle() 927 } 928 } 929 930 impl AsRawHandle for File { 931 fn as_raw_handle(&self) -> RawHandle { 932 self.as_inner().as_raw_handle() 933 } 934 } 935 936 impl IntoRawHandle for File { 937 fn into_raw_handle(self) -> RawHandle { 938 self.into_inner().into_raw_handle() 939 } 940 } 941 942 impl FromRawHandle for File { 943 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 { 951 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 { 963 pub fn size(&self) -> u64 { 964 self.file_size 965 } 966 967 pub fn perm(&self) -> FilePermissions { 968 FilePermissions { 969 attrs: self.attributes, 970 } 971 } 972 973 pub fn attrs(&self) -> u32 { 974 self.attributes 975 } 976 977 pub fn file_type(&self) -> FileType { 978 FileType::new(self.attributes, self.reparse_tag) 979 } 980 981 pub fn modified(&self) -> io::Result<SystemTime> { 982 Ok(SystemTime::from(self.last_write_time)) 983 } 984 985 pub fn accessed(&self) -> io::Result<SystemTime> { 986 Ok(SystemTime::from(self.last_access_time)) 987 } 988 989 pub fn created(&self) -> io::Result<SystemTime> { 990 Ok(SystemTime::from(self.creation_time)) 991 } 992 993 pub fn modified_u64(&self) -> u64 { 994 to_u64(&self.last_write_time) 995 } 996 997 pub fn accessed_u64(&self) -> u64 { 998 to_u64(&self.last_access_time) 999 } 1000 1001 pub fn created_u64(&self) -> u64 { 1002 to_u64(&self.creation_time) 1003 } 1004 1005 pub fn volume_serial_number(&self) -> Option<u32> { 1006 self.volume_serial_number 1007 } 1008 1009 pub fn number_of_links(&self) -> Option<u32> { 1010 self.number_of_links 1011 } 1012 1013 pub fn file_index(&self) -> Option<u64> { 1014 self.file_index 1015 } 1016 } 1017 impl From<c::WIN32_FIND_DATAW> for FileAttr { 1018 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 1038 fn to_u64(ft: &c::FILETIME) -> u64 { 1039 (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) 1040 } 1041 1042 impl FilePermissions { 1043 pub fn readonly(&self) -> bool { 1044 self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 1045 } 1046 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 { 1057 pub fn set_accessed(&mut self, t: SystemTime) { 1058 self.accessed = Some(t.into_inner()); 1059 } 1060 1061 pub fn set_modified(&mut self, t: SystemTime) { 1062 self.modified = Some(t.into_inner()); 1063 } 1064 1065 pub fn set_created(&mut self, t: SystemTime) { 1066 self.created = Some(t.into_inner()); 1067 } 1068 } 1069 1070 impl FileType { 1071 fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType { 1072 FileType { 1073 attributes: attrs, 1074 reparse_tag, 1075 } 1076 } 1077 pub fn is_dir(&self) -> bool { 1078 !self.is_symlink() && self.is_directory() 1079 } 1080 pub fn is_file(&self) -> bool { 1081 !self.is_symlink() && !self.is_directory() 1082 } 1083 pub fn is_symlink(&self) -> bool { 1084 self.is_reparse_point() && self.is_reparse_tag_name_surrogate() 1085 } 1086 pub fn is_symlink_dir(&self) -> bool { 1087 self.is_symlink() && self.is_directory() 1088 } 1089 pub fn is_symlink_file(&self) -> bool { 1090 self.is_symlink() && !self.is_directory() 1091 } 1092 fn is_directory(&self) -> bool { 1093 self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 1094 } 1095 fn is_reparse_point(&self) -> bool { 1096 self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 1097 } 1098 fn is_reparse_tag_name_surrogate(&self) -> bool { 1099 self.reparse_tag & 0x20000000 != 0 1100 } 1101 } 1102 1103 impl DirBuilder { 1104 pub fn new() -> DirBuilder { 1105 DirBuilder 1106 } 1107 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 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 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 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 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. 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 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 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 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 1275 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { 1276 symlink_inner(original, link, false) 1277 } 1278 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"))] 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")] 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 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 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 { 1353 fn as_flag(self) -> u32 { 1354 self as u32 1355 } 1356 } 1357 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 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 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 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 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)] 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)] 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