private void secureDelete(String path) { var emptyDirectory = new DirectoryInfo(path); if (!emptyDirectory.Exists) { throw new Exception("Could not delete the directory \"" + emptyDirectory.FullName + "\" because it does not exist anymore."); } // Cleanup folder // loop trough files and cancel if containsFiles == true foreach (var file in emptyDirectory.EnumerateFiles()) { var deleteTrashFile = SystemFunctions.MatchesIgnorePattern(file, ( Int32 )file.Length, this.Data.IgnoreEmptyFiles, this.Data.IgnoreFileList, out var delPattern); // If only one file is good, then stop. if (deleteTrashFile) { try { SystemFunctions.SecureDeleteFile(file, this.Data.DeleteMode); this.Data.AddLogMessage($"-> Successfully deleted file \"{file.FullName}\" because it matched the ignore pattern \"{delPattern}\""); } catch (Exception ex) { this.Data.AddLogMessage($"Failed to delete file \"{file.FullName}\" - Error message: \"{ex.Message}\""); var msg = "Could not delete this empty (trash) file:" + Environment.NewLine + file.FullName + Environment.NewLine + Environment.NewLine + "Error message: " + ex.Message; if (ex is REDPermissionDeniedException) { throw new REDPermissionDeniedException(msg, ex); } throw new Exception(msg, ex); } } } // End cleanup // This function will ensure that the directory is really empty before it gets deleted SystemFunctions.SecureDeleteDirectory(emptyDirectory.FullName, this.Data.DeleteMode); }
private DirectorySearchStatusTypes CheckIfDirectoryEmpty(DirectoryInfo startDir, Int32 depth) { if (this.PossibleEndlessLoop > this.Data.InfiniteLoopDetectionCount) { this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(startDir.FullName, DirectorySearchStatusTypes.Error, "Aborted - possible infinite-loop detected")); return(DirectorySearchStatusTypes.Error); } try { if (this.Data.MaxDepth != -1 && depth > this.Data.MaxDepth) { return(DirectorySearchStatusTypes.NotEmpty); } // Cancel process if the user hits stop if (this.CancellationPending) { return(DirectorySearchStatusTypes.NotEmpty); } this.FolderCount++; // update status progress bar after 100 steps: if (this.FolderCount % 100 == 0) { this.ReportProgress(this.FolderCount, $"Checking directory: {startDir.Name}"); } var containsFiles = false; // Get file list IEnumerable <FileInfo> fileList; // some directories could trigger a exception: try { fileList = startDir.EnumerateFiles(); } catch { fileList = null; } if (fileList == null) { // CF = true = folder does not get deleted: containsFiles = true; // secure way this.Data.AddLogMessage("Failed to access files in \"" + startDir.FullName + "\""); this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(startDir.FullName, DirectorySearchStatusTypes.Error, "Failed to access files")); } else { // loop trough files and cancel if containsFiles == true for (var f = 0; f < fileList.Length && !containsFiles; f++) { FileInfo file; Int32 filesize; try { file = fileList[f]; filesize = ( Int32 )file.Length; } catch { // keep folder if there is a strange file that // triggers a exception: return(DirectorySearchStatusTypes.NotEmpty); } // If only one file is good, then stop. if (!SystemFunctions.MatchesIgnorePattern(file, filesize, this.Data.IgnoreEmptyFiles, this.ignoreFileList, out _)) { containsFiles = true; } } } // If the folder does not contain any files -> get subfolders: DirectoryInfo[] subFolderList; try { subFolderList = startDir.GetDirectories(); } catch { // If we can not read the folder -> don't delete it: this.Data.AddLogMessage("Failed to access subdirectories in \"" + startDir.FullName + "\""); this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(startDir.FullName, DirectorySearchStatusTypes.Error, "Failed to access subdirectories")); return(DirectorySearchStatusTypes.Error); } // The folder is empty, break here: if (!containsFiles && !subFolderList.Any()) { return(DirectorySearchStatusTypes.Empty); } var allSubDirectoriesEmpty = true; foreach (var curDir in subFolderList) { var attribs = curDir.Attributes; // Hidden folder? var ignoreSubDirectory = this.Data.IgnoreHiddenFolders && (attribs & FileAttributes.Hidden) == FileAttributes.Hidden; ignoreSubDirectory = ignoreSubDirectory || this.Data.KeepSystemFolders && (attribs & FileAttributes.System) == FileAttributes.System; if (!ignoreSubDirectory && this.checkIfDirectoryIsOnIgnoreList(curDir)) { this.Data.AddLogMessage("Aborted scan of \"" + curDir.FullName + "\" because it is on the ignore list."); this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(curDir.FullName, DirectorySearchStatusTypes.Ignore)); ignoreSubDirectory = true; } if (!ignoreSubDirectory && (attribs & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { this.Data.AddLogMessage("Aborted scan of \"" + curDir.FullName + "\" because it is a symbolic link"); this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(curDir.FullName, DirectorySearchStatusTypes.Error, "Aborted because dir is a symbolic link")); ignoreSubDirectory = true; } // TODO: Implement more checks //else if ((attribs & FileAttributes.Device) == FileAttributes.Device) msg = "Device - Aborted - found"; //else if ((attribs & FileAttributes.Encrypted) == FileAttributes.Encrypted) msg = "Encrypted - found"; // The file will not be indexed by the operating system's content indexing service. // else if ((attribs & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed) msg = "NotContentIndexed - Device found"; //else if ((attribs & FileAttributes.Offline) == FileAttributes.Offline) msg = "Offline - found"; //else if ((attribs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) msg = "ReadOnly - found"; //else if ((attribs & FileAttributes.Temporary) == FileAttributes.Temporary) msg = "Temporary - found"; // Scan sub folder: var subFolderStatus = DirectorySearchStatusTypes.NotEmpty; if (!ignoreSubDirectory) { // JRS ADDED check for AGE of folder if (curDir.CreationTime.AddHours(this.Data.MinFolderAgeHours) < DateTime.Now) { subFolderStatus = this.CheckIfDirectoryEmpty(curDir, depth + 1); } else { this.Data.AddLogMessage(String.Format(Resources.young_folder_skipped, curDir.FullName, this.Data.MinFolderAgeHours.ToString(), curDir.CreationTime.ToString())); } // Report status to the GUI if (subFolderStatus == DirectorySearchStatusTypes.Empty) { this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(curDir.FullName, subFolderStatus)); } } // this folder is not empty: if (subFolderStatus != DirectorySearchStatusTypes.Empty || ignoreSubDirectory) { allSubDirectoriesEmpty = false; } } // All subdirectories are empty return(allSubDirectoriesEmpty && !containsFiles ? DirectorySearchStatusTypes.Empty : DirectorySearchStatusTypes.NotEmpty); } catch (Exception ex) { // Error handling if (ex is PathTooLongException) { this.PossibleEndlessLoop++; } this.Data.AddLogMessage("An unknown error occurred while trying to scan this directory: \"" + startDir.FullName + "\" - Error message: " + ex.Message); this.ReportProgress(0, new FoundEmptyDirInfoEventArgs(startDir.FullName, DirectorySearchStatusTypes.Error, ex.Message)); return(DirectorySearchStatusTypes.Error); } }