use std::ffi::OsString; use std::fs::{self, File, OpenOptions}; use std::os::windows::prelude::*; use std::path::{Path, PathBuf}; use std::{io, ptr};
use winapi::shared::minwindef::*; use winapi::shared::winerror::*; use winapi::um::errhandlingapi::*; use winapi::um::fileapi::*; use winapi::um::minwinbase::*; use winapi::um::winbase::*; use winapi::um::winnt::*;
/// Reliably removes a directory and all of its children. /// /// ```rust /// extern crate remove_dir_all; /// /// use std::fs; /// use remove_dir_all::*; /// /// fn main() { /// fs::create_dir("./temp/").unwrap(); /// remove_dir_all("./temp/").unwrap(); /// } /// ``` pubfn remove_dir_all<P: AsRef<Path>>(path: P) -> 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-<counter>". 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 path = path.as_ref(); letmut opts = OpenOptions::new();
opts.access_mode(FILE_READ_ATTRIBUTES);
opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); let file = opts.open(path)?;
(get_path(&file)?, path.metadata()?)
};
if ctx.readonly { // restore read-only flag just in case there are other hard links match fs::metadata(&path) {
Ok(metadata) => { letmut perm = metadata.permissions();
perm.set_readonly(true);
fs::set_permissions(&path, perm)?;
}
Err(ref err) if err.kind() == io::ErrorKind::NotFound => {}
err => return err.map(|_| ()),
}
}
// Try to rename the file. If it already exists, just retry with an other // filename. whilelet Err(err) = rename(file, &tmpname, false) { if err.kind() != io::ErrorKind::AlreadyExists { return Err(err);
};
tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter));
ctx.counter += 1;
}
Ok(())
}
fn rename(file: &File, new: &Path, replace: bool) -> io::Result<()> { // &self must be opened with DELETE permission use std::iter; #[cfg(target_pointer_width = "32")] const STRUCT_SIZE: usize = 12; #[cfg(target_pointer_width = "64")] const STRUCT_SIZE: usize = 20;
// FIXME: check for internal NULs in 'new' letmut data: Vec<u16> = 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; let result = SetFileInformationByHandle(
file.as_raw_handle(),
FileRenameInfo,
data.as_mut_ptr() as *mut _ as *mut _,
size as DWORD,
);
if result == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { let dir_readonly = ctx.readonly; for child in fs::read_dir(path)? { let child = child?; let child_type = child.file_type()?;
ctx.readonly = child.metadata()?.permissions().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 fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T> 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. letmut stack_buf = [0u16; 512]; letmut heap_buf = Vec::new(); unsafe { letmut 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) { 0if GetLastError() == 0 => 0, 0 => return Err(io::Error::last_os_error()),
n => n,
} as usize; if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
n *= 2;
} elseif k >= n {
n = k;
} else { return Ok(f2(&buf[..k]));
}
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.