From a11053ae087503be1b0f23331e320aca1c621cee Mon Sep 17 00:00:00 2001 From: nils <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:14:02 +0200 Subject: [PATCH] Remove orphaned remove_dir_all implementation from rust-installer I have no idea why it's here, but it's not used at all. --- Cargo.lock | 2 - src/tools/rust-installer/Cargo.toml | 4 - .../rust-installer/src/remove_dir_all.rs | 860 ------------------ 3 files changed, 866 deletions(-) delete mode 100644 src/tools/rust-installer/src/remove_dir_all.rs diff --git a/Cargo.lock b/Cargo.lock index d9e9ef9fb0f..ae12bbd8789 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2774,13 +2774,11 @@ dependencies = [ "anyhow", "clap 3.2.20", "flate2", - "lazy_static", "num_cpus", "rayon", "remove_dir_all", "tar", "walkdir", - "winapi", "xz2", ] diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml index 788e556b0c6..97734f048ab 100644 --- a/src/tools/rust-installer/Cargo.toml +++ b/src/tools/rust-installer/Cargo.toml @@ -22,7 +22,3 @@ remove_dir_all = "0.5" [dependencies.clap] features = ["derive"] version = "3.1" - -[target."cfg(windows)".dependencies] -lazy_static = "1" -winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "ioapiset", "winerror", "winioctl", "winnt"] } diff --git a/src/tools/rust-installer/src/remove_dir_all.rs b/src/tools/rust-installer/src/remove_dir_all.rs deleted file mode 100644 index 11097652865..00000000000 --- a/src/tools/rust-installer/src/remove_dir_all.rs +++ /dev/null @@ -1,860 +0,0 @@ -#![allow(non_snake_case)] - -use std::io; -use std::path::Path; - -#[cfg(not(windows))] -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - ::std::fs::remove_dir_all(path) -} - -#[cfg(windows)] -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - win::remove_dir_all(path) -} - -#[cfg(windows)] -mod win { - use winapi::ctypes::{c_uint, c_ushort}; - use winapi::shared::minwindef::{BOOL, DWORD, FALSE, FILETIME, LPVOID}; - use winapi::shared::winerror::{ - ERROR_CALL_NOT_IMPLEMENTED, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES, - }; - use winapi::um::errhandlingapi::{GetLastError, SetLastError}; - use winapi::um::fileapi::{ - CreateFileW, FindFirstFileW, FindNextFileW, GetFileInformationByHandle, - }; - use winapi::um::fileapi::{BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW}; - use winapi::um::fileapi::{FILE_BASIC_INFO, FILE_RENAME_INFO, TRUNCATE_EXISTING}; - use winapi::um::fileapi::{OPEN_ALWAYS, OPEN_EXISTING}; - use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; - use winapi::um::ioapiset::DeviceIoControl; - use winapi::um::libloaderapi::{GetModuleHandleW, GetProcAddress}; - use winapi::um::minwinbase::{ - FileBasicInfo, FileRenameInfo, FILE_INFO_BY_HANDLE_CLASS, WIN32_FIND_DATAW, - }; - use winapi::um::winbase::SECURITY_SQOS_PRESENT; - use winapi::um::winbase::{ - FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT, - }; - use winapi::um::winioctl::FSCTL_GET_REPARSE_POINT; - use winapi::um::winnt::{DELETE, FILE_ATTRIBUTE_DIRECTORY, HANDLE, LPCWSTR}; - use winapi::um::winnt::{FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_REPARSE_POINT}; - use winapi::um::winnt::{FILE_GENERIC_WRITE, FILE_WRITE_DATA, GENERIC_READ, GENERIC_WRITE}; - use winapi::um::winnt::{FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES}; - use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; - use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, IO_REPARSE_TAG_SYMLINK, LARGE_INTEGER}; - - use std::ffi::{OsStr, OsString}; - use std::io; - use std::mem; - use std::os::windows::ffi::{OsStrExt, OsStringExt}; - use std::path::{Path, PathBuf}; - use std::ptr; - use std::sync::Arc; - - pub fn remove_dir_all(path: &Path) -> io::Result<()> { - // On Windows it is not enough to just recursively remove the contents of a - // directory and then the directory itself. Deleting does not happen - // instantaneously, but is scheduled. - // To work around this, we move the file or directory to some `base_dir` - // right before deletion to avoid races. - // - // As `base_dir` we choose the parent dir of the directory we want to - // remove. We very probably have permission to create files here, as we - // already need write permission in this dir to delete the directory. And it - // should be on the same volume. - // - // To handle files with names like `CON` and `morse .. .`, and when a - // directory structure is so deep it needs long path names the path is first - // converted to a `//?/`-path with `get_path()`. - // - // To make sure we don't leave a moved file laying around if the process - // crashes before we can delete the file, we do all operations on an file - // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will - // always delete the file when the handle closes. - // - // All files are renamed to be in the `base_dir`, and have their name - // changed to "rm-". After every rename the counter is increased. - // Rename should not overwrite possibly existing files in the base dir. So - // if it fails with `AlreadyExists`, we just increase the counter and try - // again. - // - // For read-only files and directories we first have to remove the read-only - // attribute before we can move or delete them. This also removes the - // attribute from possible hardlinks to the file, so just before closing we - // restore the read-only attribute. - // - // If 'path' points to a directory symlink or junction we should not - // recursively remove the target of the link, but only the link itself. - // - // Moving and deleting is guaranteed to succeed if we are able to open the - // file with `DELETE` permission. If others have the file open we only have - // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can - // also delete the file now, but it will not disappear until all others have - // closed the file. But no-one can open the file after we have flagged it - // for deletion. - - // Open the path once to get the canonical path, file type and attributes. - let (path, metadata) = { - let mut opts = OpenOptions::new(); - opts.access_mode(FILE_READ_ATTRIBUTES); - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); - let file = File::open(path, &opts)?; - (get_path(&file)?, file.file_attr()?) - }; - - let mut ctx = RmdirContext { - base_dir: match path.parent() { - Some(dir) => dir, - None => { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "can't delete root directory", - )) - } - }, - readonly: metadata.perm().readonly(), - counter: 0, - }; - - let filetype = metadata.file_type(); - if filetype.is_dir() { - remove_dir_all_recursive(path.as_ref(), &mut ctx) - } else if filetype.is_symlink_dir() { - remove_item(path.as_ref(), &mut ctx) - } else { - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Not a directory", - )) - } - } - - fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let star = p.join("*"); - let path = to_u16s(&star)?; - - unsafe { - let mut wfd = mem::zeroed(); - let find_handle = FindFirstFileW(path.as_ptr(), &mut wfd); - if find_handle != INVALID_HANDLE_VALUE { - Ok(ReadDir { - handle: FindNextFileHandle(find_handle), - root: Arc::new(root), - first: Some(wfd), - }) - } else { - Err(io::Error::last_os_error()) - } - } - } - - struct RmdirContext<'a> { - base_dir: &'a Path, - readonly: bool, - counter: u64, - } - - fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - let dir_readonly = ctx.readonly; - for child in readdir(path)? { - let child = child?; - let child_type = child.file_type()?; - ctx.readonly = child.metadata()?.perm().readonly(); - if child_type.is_dir() { - remove_dir_all_recursive(&child.path(), ctx)?; - } else { - remove_item(&child.path().as_ref(), ctx)?; - } - } - ctx.readonly = dir_readonly; - remove_item(path, ctx) - } - - fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - if !ctx.readonly { - let mut opts = OpenOptions::new(); - opts.access_mode(DELETE); - opts.custom_flags( - FILE_FLAG_BACKUP_SEMANTICS | // delete directory - FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink - FILE_FLAG_DELETE_ON_CLOSE, - ); - let file = File::open(path, &opts)?; - move_item(&file, ctx) - } else { - // remove read-only permision - set_perm(&path, FilePermissions::new())?; - // move and delete file, similar to !readonly. - // only the access mode is different. - let mut opts = OpenOptions::new(); - opts.access_mode(DELETE | FILE_WRITE_ATTRIBUTES); - opts.custom_flags( - FILE_FLAG_BACKUP_SEMANTICS - | FILE_FLAG_OPEN_REPARSE_POINT - | FILE_FLAG_DELETE_ON_CLOSE, - ); - let file = File::open(path, &opts)?; - move_item(&file, ctx)?; - // restore read-only flag just in case there are other hard links - let mut perm = FilePermissions::new(); - perm.set_readonly(true); - let _ = file.set_perm(perm); // ignore if this fails - Ok(()) - } - } - - macro_rules! compat_fn { - ($module:ident: $( - fn $symbol:ident($($argname:ident: $argtype:ty),*) - -> $rettype:ty { - $($body:expr);* - } - )*) => ($( - #[allow(unused_variables)] - unsafe fn $symbol($($argname: $argtype),*) -> $rettype { - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::mem; - use std::ffi::CString; - type F = unsafe extern "system" fn($($argtype),*) -> $rettype; - - lazy_static! { static ref PTR: AtomicUsize = AtomicUsize::new(0);} - - fn lookup(module: &str, symbol: &str) -> Option { - let mut module: Vec = module.encode_utf16().collect(); - module.push(0); - let symbol = CString::new(symbol).unwrap(); - unsafe { - let handle = GetModuleHandleW(module.as_ptr()); - match GetProcAddress(handle, symbol.as_ptr()) as usize { - 0 => None, - n => Some(n), - } - } - } - - fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, - fallback: usize) -> usize { - let value = lookup(module, symbol).unwrap_or(fallback); - ptr.store(value, Ordering::SeqCst); - value - } - - fn load() -> usize { - store_func(&PTR, stringify!($module), stringify!($symbol), fallback as usize) - } - unsafe extern "system" fn fallback($($argname: $argtype),*) - -> $rettype { - $($body);* - } - - let addr = match PTR.load(Ordering::SeqCst) { - 0 => load(), - n => n, - }; - mem::transmute::(addr)($($argname),*) - } - )*) - } - - compat_fn! { - kernel32: - fn GetFinalPathNameByHandleW(_hFile: HANDLE, - _lpszFilePath: LPCWSTR, - _cchFilePath: DWORD, - _dwFlags: DWORD) -> DWORD { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - fn SetFileInformationByHandle(_hFile: HANDLE, - _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - _lpFileInformation: LPVOID, - _dwBufferSize: DWORD) -> BOOL { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - } - - fn cvt(i: i32) -> io::Result { - if i == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(i) - } - } - - fn to_u16s>(s: S) -> io::Result> { - fn inner(s: &OsStr) -> io::Result> { - let mut maybe_result: Vec = s.encode_wide().collect(); - if maybe_result.iter().any(|&u| u == 0) { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "strings passed to WinAPI cannot contain NULs", - )); - } - maybe_result.push(0); - Ok(maybe_result) - } - inner(s.as_ref()) - } - - fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { - match v.iter().position(|c| *c == 0) { - // don't include the 0 - Some(i) => &v[..i], - None => v, - } - } - - fn fill_utf16_buf(mut f1: F1, f2: F2) -> io::Result - where - F1: FnMut(*mut u16, DWORD) -> DWORD, - F2: FnOnce(&[u16]) -> T, - { - // Start off with a stack buf but then spill over to the heap if we end up - // needing more space. - let mut stack_buf = [0u16; 512]; - let mut heap_buf = Vec::new(); - unsafe { - let mut n = stack_buf.len(); - loop { - let buf = if n <= stack_buf.len() { - &mut stack_buf[..] - } else { - let extra = n - heap_buf.len(); - heap_buf.reserve(extra); - heap_buf.set_len(n); - &mut heap_buf[..] - }; - - // This function is typically called on windows API functions which - // will return the correct length of the string, but these functions - // also return the `0` on error. In some cases, however, the - // returned "correct length" may actually be 0! - // - // To handle this case we call `SetLastError` to reset it to 0 and - // then check it again if we get the "0 error value". If the "last - // error" is still 0 then we interpret it as a 0 length buffer and - // not an actual error. - SetLastError(0); - let k = match f1(buf.as_mut_ptr(), n as DWORD) { - 0 if GetLastError() == 0 => 0, - 0 => return Err(io::Error::last_os_error()), - n => n, - } as usize; - if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER { - n *= 2; - } else if k >= n { - n = k; - } else { - return Ok(f2(&buf[..k])); - } - } - } - } - - #[derive(Clone, PartialEq, Eq, Debug, Default)] - struct FilePermissions { - readonly: bool, - } - - impl FilePermissions { - fn new() -> FilePermissions { - Default::default() - } - fn readonly(&self) -> bool { - self.readonly - } - fn set_readonly(&mut self, readonly: bool) { - self.readonly = readonly - } - } - - #[derive(Clone)] - struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: u32, - access_mode: Option, - attributes: DWORD, - share_mode: DWORD, - security_qos_flags: DWORD, - security_attributes: usize, // FIXME: should be a reference - } - - impl OpenOptions { - fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - custom_flags: 0, - access_mode: None, - share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - attributes: 0, - security_qos_flags: 0, - security_attributes: 0, - } - } - fn custom_flags(&mut self, flags: u32) { - self.custom_flags = flags; - } - fn access_mode(&mut self, access_mode: u32) { - self.access_mode = Some(access_mode); - } - - fn get_access_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - - match (self.read, self.write, self.append, self.access_mode) { - (_, _, _, Some(mode)) => Ok(mode), - (true, false, false, None) => Ok(GENERIC_READ), - (false, true, false, None) => Ok(GENERIC_WRITE), - (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE), - (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA), - (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)), - (false, false, false, None) => { - Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)) - } - } - } - - fn get_creation_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => OPEN_EXISTING, - (true, false, false) => OPEN_ALWAYS, - (false, true, false) => TRUNCATE_EXISTING, - (true, true, false) => CREATE_ALWAYS, - (_, _, true) => CREATE_NEW, - }) - } - - fn get_flags_and_attributes(&self) -> DWORD { - self.custom_flags - | self.attributes - | self.security_qos_flags - | if self.security_qos_flags != 0 { - SECURITY_SQOS_PRESENT - } else { - 0 - } - | if self.create_new { - FILE_FLAG_OPEN_REPARSE_POINT - } else { - 0 - } - } - } - - struct File { - handle: Handle, - } - - impl File { - fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = to_u16s(path)?; - let handle = unsafe { - CreateFileW( - path.as_ptr(), - opts.get_access_mode()?, - opts.share_mode, - opts.security_attributes as *mut _, - opts.get_creation_mode()?, - opts.get_flags_and_attributes(), - ptr::null_mut(), - ) - }; - if handle == INVALID_HANDLE_VALUE { - Err(io::Error::last_os_error()) - } else { - Ok(File { - handle: Handle::new(handle), - }) - } - } - - fn file_attr(&self) -> io::Result { - unsafe { - let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(GetFileInformationByHandle(self.handle.raw(), &mut info))?; - let mut attr = FileAttr { - attributes: info.dwFileAttributes, - creation_time: info.ftCreationTime, - last_access_time: info.ftLastAccessTime, - last_write_time: info.ftLastWriteTime, - file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64), - reparse_tag: 0, - }; - if attr.is_reparse_point() { - let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; - } - } - Ok(attr) - } - } - - fn set_attributes(&self, attr: DWORD) -> io::Result<()> { - let zero: LARGE_INTEGER = unsafe { mem::zeroed() }; - - let mut info = FILE_BASIC_INFO { - CreationTime: zero, // do not change - LastAccessTime: zero, // do not change - LastWriteTime: zero, // do not change - ChangeTime: zero, // do not change - FileAttributes: attr, - }; - let size = mem::size_of_val(&info); - cvt(unsafe { - SetFileInformationByHandle( - self.handle.raw(), - FileBasicInfo, - &mut info as *mut _ as *mut _, - size as DWORD, - ) - })?; - Ok(()) - } - - fn rename(&self, new: &Path, replace: bool) -> io::Result<()> { - // &self must be opened with DELETE permission - use std::iter; - #[cfg(target_arch = "x86")] - const STRUCT_SIZE: usize = 12; - #[cfg(target_arch = "x86_64")] - const STRUCT_SIZE: usize = 20; - - // FIXME: check for internal NULs in 'new' - let mut data: Vec = iter::repeat(0u16) - .take(STRUCT_SIZE / 2) - .chain(new.as_os_str().encode_wide()) - .collect(); - data.push(0); - let size = data.len() * 2; - - unsafe { - // Thanks to alignment guarantees on Windows this works - // (8 for 32-bit and 16 for 64-bit) - let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO; - // The type of ReplaceIfExists is BOOL, but it actually expects a - // BOOLEAN. This means true is -1, not c::TRUE. - (*info).ReplaceIfExists = if replace { -1 } else { FALSE }; - (*info).RootDirectory = ptr::null_mut(); - (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD; - cvt(SetFileInformationByHandle( - self.handle().raw(), - FileRenameInfo, - data.as_mut_ptr() as *mut _ as *mut _, - size as DWORD, - ))?; - Ok(()) - } - } - fn set_perm(&self, perm: FilePermissions) -> io::Result<()> { - let attr = self.file_attr()?.attributes; - if perm.readonly == (attr & FILE_ATTRIBUTE_READONLY != 0) { - Ok(()) - } else if perm.readonly { - self.set_attributes(attr | FILE_ATTRIBUTE_READONLY) - } else { - self.set_attributes(attr & !FILE_ATTRIBUTE_READONLY) - } - } - - fn handle(&self) -> &Handle { - &self.handle - } - - fn reparse_point<'a>( - &self, - space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE], - ) -> io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> { - unsafe { - let mut bytes = 0; - cvt({ - DeviceIoControl( - self.handle.raw(), - FSCTL_GET_REPARSE_POINT, - ptr::null_mut(), - 0, - space.as_mut_ptr() as *mut _, - space.len() as DWORD, - &mut bytes, - ptr::null_mut(), - ) - })?; - Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER))) - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - enum FileType { - Dir, - File, - SymlinkFile, - SymlinkDir, - ReparsePoint, - MountPoint, - } - - impl FileType { - fn new(attrs: DWORD, reparse_tag: DWORD) -> FileType { - match ( - attrs & FILE_ATTRIBUTE_DIRECTORY != 0, - attrs & FILE_ATTRIBUTE_REPARSE_POINT != 0, - reparse_tag, - ) { - (false, false, _) => FileType::File, - (true, false, _) => FileType::Dir, - (false, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile, - (true, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir, - (true, true, IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint, - (_, true, _) => FileType::ReparsePoint, - // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is - // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint - // to indicate it is something symlink-like, but not something you can follow. - } - } - - fn is_dir(&self) -> bool { - *self == FileType::Dir - } - fn is_symlink_dir(&self) -> bool { - *self == FileType::SymlinkDir || *self == FileType::MountPoint - } - } - - impl DirEntry { - fn new(root: &Arc, wfd: &WIN32_FIND_DATAW) -> Option { - let first_bytes = &wfd.cFileName[0..3]; - if first_bytes.starts_with(&[46, 0]) || first_bytes.starts_with(&[46, 46, 0]) { - None - } else { - Some(DirEntry { - root: root.clone(), - data: *wfd, - }) - } - } - - fn path(&self) -> PathBuf { - self.root.join(&self.file_name()) - } - - fn file_name(&self) -> OsString { - let filename = truncate_utf16_at_nul(&self.data.cFileName); - OsString::from_wide(filename) - } - - fn file_type(&self) -> io::Result { - Ok(FileType::new( - self.data.dwFileAttributes, - /* reparse_tag = */ self.data.dwReserved0, - )) - } - - fn metadata(&self) -> io::Result { - Ok(FileAttr { - attributes: self.data.dwFileAttributes, - creation_time: self.data.ftCreationTime, - last_access_time: self.data.ftLastAccessTime, - last_write_time: self.data.ftLastWriteTime, - file_size: ((self.data.nFileSizeHigh as u64) << 32) - | (self.data.nFileSizeLow as u64), - reparse_tag: if self.data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 { - // reserved unless this is a reparse point - self.data.dwReserved0 - } else { - 0 - }, - }) - } - } - - struct DirEntry { - root: Arc, - data: WIN32_FIND_DATAW, - } - - struct ReadDir { - handle: FindNextFileHandle, - root: Arc, - first: Option, - } - - impl Iterator for ReadDir { - type Item = io::Result; - fn next(&mut self) -> Option> { - if let Some(first) = self.first.take() { - if let Some(e) = DirEntry::new(&self.root, &first) { - return Some(Ok(e)); - } - } - unsafe { - let mut wfd = mem::zeroed(); - loop { - if FindNextFileW(self.handle.0, &mut wfd) == 0 { - if GetLastError() == ERROR_NO_MORE_FILES { - return None; - } else { - return Some(Err(io::Error::last_os_error())); - } - } - if let Some(e) = DirEntry::new(&self.root, &wfd) { - return Some(Ok(e)); - } - } - } - } - } - - #[derive(Clone)] - struct FileAttr { - attributes: DWORD, - creation_time: FILETIME, - last_access_time: FILETIME, - last_write_time: FILETIME, - file_size: u64, - reparse_tag: DWORD, - } - - impl FileAttr { - fn perm(&self) -> FilePermissions { - FilePermissions { - readonly: self.attributes & FILE_ATTRIBUTE_READONLY != 0, - } - } - - fn file_type(&self) -> FileType { - FileType::new(self.attributes, self.reparse_tag) - } - - fn is_reparse_point(&self) -> bool { - self.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 - } - } - - #[repr(C)] - struct REPARSE_DATA_BUFFER { - ReparseTag: c_uint, - ReparseDataLength: c_ushort, - Reserved: c_ushort, - rest: (), - } - - const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024; - - /// An owned container for `HANDLE` object, closing them on Drop. - /// - /// All methods are inherited through a `Deref` impl to `RawHandle` - struct Handle(RawHandle); - - use std::ops::Deref; - - /// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference - /// as well as Rust-y methods. - /// - /// This does **not** drop the handle when it goes out of scope, use `Handle` - /// instead for that. - #[derive(Copy, Clone)] - struct RawHandle(HANDLE); - - unsafe impl Send for RawHandle {} - unsafe impl Sync for RawHandle {} - - impl Handle { - fn new(handle: HANDLE) -> Handle { - Handle(RawHandle::new(handle)) - } - } - - impl Deref for Handle { - type Target = RawHandle; - fn deref(&self) -> &RawHandle { - &self.0 - } - } - - impl Drop for Handle { - fn drop(&mut self) { - unsafe { - let _ = CloseHandle(self.raw()); - } - } - } - - impl RawHandle { - fn new(handle: HANDLE) -> RawHandle { - RawHandle(handle) - } - - fn raw(&self) -> HANDLE { - self.0 - } - } - - struct FindNextFileHandle(HANDLE); - - fn get_path(f: &File) -> io::Result { - fill_utf16_buf( - |buf, sz| unsafe { - GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, VOLUME_NAME_DOS) - }, - |buf| PathBuf::from(OsString::from_wide(buf)), - ) - } - - fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> { - let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter}); - ctx.counter += 1; - // Try to rename the file. If it already exists, just retry with an other - // filename. - while let Err(err) = file.rename(tmpname.as_ref(), false) { - if err.kind() != io::ErrorKind::AlreadyExists { - return Err(err); - }; - tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter)); - ctx.counter += 1; - } - Ok(()) - } - - fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { - let mut opts = OpenOptions::new(); - opts.access_mode(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES); - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(path, &opts)?; - file.set_perm(perm) - } - - const VOLUME_NAME_DOS: DWORD = 0x0; -}