Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  fs.rs   Sprache: unbekannt

 
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::*;

pub const VOLUME_NAME_DOS: DWORD = 0x0;

struct RmdirContext<'a> {
    base_dir: &'a Path,
    readonly: bool,
    counter: u64,
}

/// 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();
/// }
/// ```
pub fn 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();
        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 = opts.open(path)?;
        (get_path(&file)?, path.metadata()?)
    };

    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.permissions().readonly(),
        counter: 0,
    };

    let filetype = metadata.file_type();
    if filetype.is_dir() {
        if !filetype.is_symlink() {
            remove_dir_all_recursive(path.as_ref(), &mut ctx)
        } else {
            remove_item(path.as_ref(), &mut ctx)
        }
    } else {
        Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "Not a directory",
        ))
    }
}

fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
    if ctx.readonly {
        // remove read-only permision
        let mut permissions = path.metadata()?.permissions();
        permissions.set_readonly(false);

        fs::set_permissions(path, permissions)?;
    }

    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 = opts.open(path)?;
    move_item(&file, ctx)?;

    if ctx.readonly {
        // restore read-only flag just in case there are other hard links
        match fs::metadata(&path) {
            Ok(metadata) => {
                let mut 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(|_| ()),
        }
    }

    Ok(())
}

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) = 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'
    let mut 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 get_path(f: &File) -> io::Result<PathBuf> {
    fill_utf16_buf(
        |buf, sz| unsafe { GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, VOLUME_NAME_DOS) },
        |buf| PathBuf::from(OsString::from_wide(buf)),
    )
}

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.
    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]));
            }
        }
    }
}

[ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge