private static IErasureMethod ErasureMethodFromNameOrGuid(string param) { try { return(Host.Instance.ErasureMethods[new Guid(param)]); } catch (FormatException) { //Invalid GUID. Check every registered erasure method for the name string upperParam = param.ToUpperInvariant(); IErasureMethod result = null; foreach (IErasureMethod method in Host.Instance.ErasureMethods) { if (method.Name.ToUpperInvariant() == upperParam) { if (result == null) { result = method; } else { throw new ArgumentException(S._("Ambiguous erasure method name: {0} " + "identifies more than one erasure method.", param)); } } } } throw new ArgumentException(S._("The provided Erasure Method '{0}' does not exist.", param)); }
private void OnMethodUnregistered(object sender, EventArgs e) { IErasureMethod method = (IErasureMethod)sender; foreach (IErasureMethod obj in eraseFilesMethod.Items) { if (obj.Guid == method.Guid) { eraseFilesMethod.Items.Remove(obj); break; } } foreach (IErasureMethod obj in eraseDriveMethod.Items) { if (obj.Guid == method.Guid) { eraseDriveMethod.Items.Remove(obj); break; } } if (eraseFilesMethod.SelectedIndex == -1) { eraseFilesMethod.SelectedIndex = 0; } if (eraseDriveMethod.SelectedIndex == -1) { eraseDriveMethod.SelectedIndex = 0; } }
public override void EraseFileSystemObject(StreamInfo info, IErasureMethod method, ErasureMethodProgressFunction callback) { //Create the file stream, and call the erasure method to write to //the stream. long fileArea = GetFileArea(info); using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write, FileShare.None, FileOptions.WriteThrough)) { //Set the end of the stream after the wrap-round the cluster size strm.SetLength(fileArea); //If the stream is empty, there's nothing to overwrite. Continue //to the next entry if (strm.Length != 0) { //Then erase the file. method.Erase(strm, long.MaxValue, Host.Instance.Prngs.ActivePrng, callback); } //Set the length of the file to 0. strm.Seek(0, SeekOrigin.Begin); strm.SetLength(0); } }
private void OnMethodRegistered(object sender, EventArgs e) { IErasureMethod method = (IErasureMethod)sender; eraseFilesMethod.Items.Add(method); if (method is IDriveErasureMethod) { eraseDriveMethod.Items.Add(method); } }
public override void EraseFileSystemObject(StreamInfo info, IErasureMethod method, ErasureMethodProgressFunction callback) { //Check if the file fits in one cluster - if it does it may be MFT resident //TODO: any more deterministic way of finding out? VolumeInfo volume = VolumeInfo.FromMountPoint(info.DirectoryName); if (info.Length < NtfsApi.GetMftRecordSegmentSize(volume)) { //Yes it does, erase exactly to the file length using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write, FileShare.None)) { method.Erase(strm, long.MaxValue, Host.Instance.Prngs.ActivePrng, null); } } //Create the file stream, and call the erasure method to write to //the stream. long fileArea = GetFileArea(info); //If the stream is empty, there's nothing to overwrite. Continue //to the next entry if (fileArea == 0) { return; } using (FileStream strm = info.Open(FileMode.Open, FileAccess.Write, FileShare.None, FileOptions.WriteThrough)) { //Set the end of the stream after the wrap-round the cluster size strm.SetLength(fileArea); //Then erase the file. method.Erase(strm, long.MaxValue, Host.Instance.Prngs.ActivePrng, callback); //Set the length of the file to 0. strm.Seek(0, SeekOrigin.Begin); strm.SetLength(0); } }
public override void EraseClusterTips(VolumeInfo info, IErasureMethod method, ClusterTipsSearchProgress searchCallback, ClusterTipsEraseProgress eraseCallback) { //List all the files which can be erased. List <string> files = new List <string>(); if (!info.IsMounted) { throw new InvalidOperationException(S._("Could not erase cluster tips in {0} " + "as the volume is not mounted.", info.VolumeId)); } ListFiles(info.MountPoints[0], files, searchCallback); //For every file, erase the cluster tips. for (int i = 0, j = files.Count; i != j; ++i) { //Get the file attributes for restoring later StreamInfo streamInfo = new StreamInfo(files[i]); if (!streamInfo.Exists) { continue; } try { EraseFileClusterTips(streamInfo, method); } catch (UnauthorizedAccessException) { Logger.Log(S._("{0} did not have its cluster tips erased because you do not " + "have the required permissions to erase the file cluster tips.", files[i]), LogLevel.Information); } catch (IOException e) { Logger.Log(S._("{0} did not have its cluster tips erased. The error returned " + "was: {1}", files[i], e.Message), LogLevel.Warning); } eraseCallback(i, files.Count, files[i]); } }
/// <summary> /// Parses the command line for erasure targets and returns them as /// a Task object. /// </summary> /// <param name="arguments">The arguments specified on the command line.</param> /// <returns>The task represented on the command line.</returns> private static Task TaskFromCommandLine(TaskArguments arguments) { //Create the task Task task = new Task(); //Get the erasure method the user wants to use IErasureMethod method = string.IsNullOrEmpty(arguments.ErasureMethod) ? ErasureMethodRegistrar.Default : ErasureMethodFromNameOrGuid(arguments.ErasureMethod); //Define the schedule if (!string.IsNullOrEmpty(arguments.Schedule)) { switch (arguments.Schedule.ToUpperInvariant()) { case "NOW": task.Schedule = Schedule.RunNow; break; case "MANUALLY": task.Schedule = Schedule.RunManually; break; case "RESTART": task.Schedule = Schedule.RunOnRestart; break; default: throw new ArgumentException( S._("Unknown schedule type: {0}", arguments.Schedule), "/schedule"); } } //Parse the rest of the command line parameters as target expressions. foreach (string argument in arguments.PositionalArguments) { IErasureTarget selectedTarget = null; //Iterate over every defined erasure target foreach (IErasureTarget target in Host.Instance.ErasureTargetFactories) { //See if this argument can be handled by the target's configurer IErasureTargetConfigurer configurer = target.Configurer; if (configurer.ProcessArgument(argument)) { //Check whether a target has been set (implicitly: check whether two //configurers can process the argument) if (selectedTarget == null) { configurer.SaveTo(target); selectedTarget = target; } else { //Yes, it is an ambiguity. Throw an error. throw new ArgumentException(S._("Ambiguous argument: {0} can be " + "handled by more than one erasure target.", argument)); } } } //Check whether a target has been made from parsing the entry. if (selectedTarget == null) { Console.WriteLine(S._("Unknown argument: {0}, skipped.", argument)); } else { selectedTarget.Method = method; task.Targets.Add(selectedTarget); } } //Check the number of tasks in the task. if (task.Targets.Count == 0) { throw new ArgumentException(S._("Tasks must contain at least one erasure target.")); } return(task); }
private void EraseUnusedSpace(VolumeInfo volInfo, DirectoryInfo info, IFileSystem fsInfo, IErasureMethod method) { ProgressManager mainProgress = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(mainProgress, EraseClusterTips ? 0.8f : 0.9f, S._("Erasing unused space..."))); //Continue creating files while there is free space. while (volInfo.AvailableFreeSpace > 0) { //Generate a non-existant file name string currFile = FileSystemBase.GenerateRandomFileName(info, 18); //Create the stream FileStream stream = new FileStream(currFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough); try { //Set the length of the file to be the amount of free space left //or the maximum size of one of these dumps. mainProgress.Total = mainProgress.Completed + method.CalculateEraseDataSize(null, volInfo.AvailableFreeSpace); long streamLength = Math.Min(PassBasedErasureMethod.FreeSpaceFileUnit, volInfo.AvailableFreeSpace); //Handle IO exceptions gracefully, because the filesystem //may require more space than demanded by us for file allocation. while (true) { try { stream.SetLength(streamLength); break; } catch (IOException) { if (streamLength > volInfo.ClusterSize) { streamLength -= volInfo.ClusterSize; } else { throw; } } } //Then run the erase task method.Erase(stream, long.MaxValue, Host.Instance.Prngs.ActivePrng, delegate(long lastWritten, long totalData, int currentPass) { mainProgress.Completed += lastWritten; mainProgress.Tag = new int[] { currentPass, method.Passes }; if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } } ); } finally { stream.Close(); fsInfo.ResetFileTimes(new FileInfo(currFile)); } } //Mark the main bulk of the progress as complete mainProgress.MarkComplete(); }
public override void Execute() { //Check for sufficient privileges to run the unused space erasure. if (!Security.IsAdministrator()) { if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= new Version(6, 0)) { Logger.Log(S._("The program does not have the required permissions to erase " + "the unused space on disk. Run the program as an administrator and retry " + "the operation."), LogLevel.Error); } else { Logger.Log(S._("The program does not have the required permissions to erase " + "the unused space on disk."), LogLevel.Error); } return; } //Check whether System Restore has any available checkpoints. if (SystemRestore.GetInstances().Count != 0) { Logger.Log(S._("This computer has had System Restore or Volume Shadow Copies " + "enabled. This may allow copies of files stored on the disk to be recovered " + "and pose a security concern.", Drive), LogLevel.Warning); } //If the user is under disk quotas, log a warning message if (VolumeInfo.FromMountPoint(Drive).HasQuota) { Logger.Log(S._("The drive {0} has disk quotas active. This will prevent the " + "complete erasure of unused space and may pose a security concern.", Drive), LogLevel.Warning); } //Get the erasure method if the user specified he wants the default. IErasureMethod method = EffectiveMethod; //Make a folder to dump our temporary files in DirectoryInfo info = new DirectoryInfo(Drive); VolumeInfo volInfo = VolumeInfo.FromMountPoint(Drive); IFileSystem fsManager = Host.Instance.FileSystems[volInfo]; //Start sampling the speed of the task. Progress = new SteppedProgressManager(); //Erase the cluster tips of every file on the drive. if (EraseClusterTips) { //Define the callback handlers ProgressManager tipSearch = new ProgressManager(); tipSearch.MarkIndeterminate(); Progress.Steps.Add(new SteppedProgressManagerStep(tipSearch, 0.0f, S._("Searching for files' cluster tips..."))); ClusterTipsSearchProgress searchProgress = delegate(string path) { if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } tipSearch.Tag = path; }; ProgressManager tipProgress = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(tipProgress, 0.1f, S._("Erasing cluster tips..."))); ClusterTipsEraseProgress eraseProgress = delegate(int currentFile, int totalFiles, string currentFilePath) { tipSearch.MarkComplete(); tipProgress.Total = totalFiles; tipProgress.Completed = currentFile; tipProgress.Tag = currentFilePath; if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } }; //Start counting statistics fsManager.EraseClusterTips(VolumeInfo.FromMountPoint(Drive), method, searchProgress, eraseProgress); tipProgress.MarkComplete(); } bool lowDiskSpaceNotifications = Shell.LowDiskSpaceNotificationsEnabled; info = info.CreateSubdirectory(Path.GetFileName( FileSystemBase.GenerateRandomFileName(info, 18))); try { //Set the folder's compression flag off since we want to use as much //space as possible if (info.IsCompressed()) { info.Uncompress(); } //Disable the low disk space notifications Shell.LowDiskSpaceNotificationsEnabled = false; //Fill the disk EraseUnusedSpace(volInfo, info, fsManager, method); //Erase old resident file system table files ProgressManager residentProgress = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(residentProgress, 0.05f, S._("Old resident file system table files"))); fsManager.EraseOldFileSystemResidentFiles(volInfo, info, method, delegate(int currentFile, int totalFiles) { residentProgress.Completed = currentFile; residentProgress.Total = totalFiles; if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } } ); residentProgress.MarkComplete(); } finally { //Remove the folder holding all our temporary files. ProgressManager tempFiles = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(tempFiles, 0.0f, S._("Removing temporary files..."))); fsManager.DeleteFolder(info, true); tempFiles.MarkComplete(); //Reset the low disk space notifications Shell.LowDiskSpaceNotificationsEnabled = lowDiskSpaceNotifications; } //Then clean the old file system entries ProgressManager structureProgress = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(structureProgress, 0.05f, S._("Erasing unused directory structures..."))); fsManager.EraseDirectoryStructures(volInfo, delegate(int currentFile, int totalFiles) { if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } //Compute the progress structureProgress.Total = totalFiles; structureProgress.Completed = currentFile; } ); structureProgress.MarkComplete(); Progress = null; }
public override bool SupportsMethod(IErasureMethod method) { return(method == ErasureMethodRegistrar.Default || method is IDriveErasureMethod); }
public abstract void EraseOldFileSystemResidentFiles(Util.VolumeInfo volume, DirectoryInfo tempDirectory, IErasureMethod method, FileSystemEntriesEraseProgress callback);
public override void EraseOldFileSystemResidentFiles(VolumeInfo volume, DirectoryInfo tempDirectory, IErasureMethod method, FileSystemEntriesEraseProgress callback) { //Nothing to be done here. FAT doesn't store files in its FAT. }
/// <summary> /// Erases the cluster tips of the given file. /// </summary> /// <param name="stream">The stream to erase.</param> /// <param name="method">The erasure method to use.</param> private void EraseFileClusterTips(StreamInfo streamInfo, IErasureMethod method) { //Get the file access times DateTime lastAccess = streamInfo.LastAccessTime; DateTime lastWrite = streamInfo.LastWriteTime; DateTime created = streamInfo.CreationTime; DateTime updated = streamInfo.LastAccessTime; //Get the file attributes FileAttributes attributes = streamInfo.Attributes; //And get the file lengths to know how much to overwrite long fileArea = GetFileArea(streamInfo); long fileLength = streamInfo.Length; //If the file length equals the file area there is no cluster tip to overwrite if (fileArea == fileLength) { return; } //Otherwise, unset any read-only flags, create the stream, lengthen the //file, then tell the erasure method to erase the cluster tips. try { streamInfo.Attributes = FileAttributes.Normal; FileStream stream = streamInfo.Open(FileMode.Open, FileAccess.Write, FileShare.None, FileOptions.WriteThrough); try { stream.SetLength(fileArea); stream.Seek(fileLength, SeekOrigin.Begin); //Erase the file method.Erase(stream, long.MaxValue, Host.Instance.Prngs.ActivePrng, null); } finally { //Make sure the file length is restored! stream.SetLength(fileLength); //Then destroy the stream stream.Close(); } } catch (ArgumentException e) { //This is an undocumented exception: when the path we are setting //cannot be accessed (ERROR_ACCESS_DENIED is returned) an //ArgumentException is raised (no idea why!) throw new UnauthorizedAccessException(e.Message, e); } finally { //Reset the file attributes if (streamInfo.Attributes != attributes) { streamInfo.Attributes = attributes; } //Reset the file times //Removed MinTimestamp as first param GT31MAR2013 streamInfo.SetTimes(updated, created, lastWrite, lastAccess); } }
/// <summary> /// Attempts to erase a stream, trying to close all open handles if a process has /// a lock on the file. /// </summary> /// <param name="fsManager">The file system provider used to erase the stream.</param> /// <param name="method">The erasure method to use to erase the stream.</param> /// <param name="info">The stream to erase.</param> /// <param name="callback">The erasure progress callback.</param> private void TryEraseStream(IFileSystem fsManager, IErasureMethod method, StreamInfo info, ErasureMethodProgressFunction callback) { for (int i = 0; ; ++i) { try { //Make sure the file does not have any attributes which may affect //the erasure process if ((info.Attributes & FileAttributes.Compressed) != 0 || (info.Attributes & FileAttributes.Encrypted) != 0 || (info.Attributes & FileAttributes.SparseFile) != 0) { //Log the error Logger.Log(S._("The file {0} could not be erased because the file was " + "either compressed, encrypted or a sparse file.", info.FullName), LogLevel.Error); return; } //Do not erase reparse points, as they will cause other references to the file //to be to garbage. if ((info.Attributes & FileAttributes.ReparsePoint) == 0) { fsManager.EraseFileSystemObject(info, method, callback); } else { Logger.Log(S._("The file {0} is a hard link or a symbolic link thus the " + "contents of the file was not erased.", info.FullName), LogLevel.Notice); } return; } catch (FileNotFoundException) { Logger.Log(S._("The file {0} was not erased because it was " + "deleted before it could be erased.", info.FullName), LogLevel.Information); } catch (DirectoryNotFoundException) { Logger.Log(S._("The file {0} was not erased because the containing " + "directory was deleted before it could be erased.", info.FullName), LogLevel.Information); } catch (SharingViolationException) { if (!Host.Instance.Settings.ForceUnlockLockedFiles) { throw; } //Try closing all open handles. If it succeeds, we can run the erase again. //To prevent Eraser from deadlocking, we will only attempt this once. Some //programs may be aggressive and keep a handle open in a tight loop. List <OpenHandle> remainingHandles = OpenHandle.Close(info.FullName); if (i == 0 && remainingHandles.Count == 0) { continue; } //Either we could not close all instances, or we already tried twice. Report //the error. string processes = string.Empty; { StringBuilder processStr = new StringBuilder(); foreach (OpenHandle handle in remainingHandles) { try { processStr.AppendFormat( System.Globalization.CultureInfo.InvariantCulture, "{0}, ", handle.Process.MainModule.FileName); } catch (System.ComponentModel.Win32Exception) { processStr.AppendFormat( System.Globalization.CultureInfo.InvariantCulture, "Process ID {0}, ", handle.Process.Id); } } if (processStr.Length > 2) { processes = processStr.ToString().Remove(processStr.Length - 2).Trim(); } else { processes = S._("(unknown)"); } } throw new SharingViolationException(S._( "Could not force closure of file \"{0}\" {1}", info.FileName, S._("(locked by {0})", processes))); } } }
/// <summary> /// Erases the provided stream, and updates progress using the provided /// progress manager. /// </summary> /// <param name="info">The information regarding the stream that needs erasure.</param> /// <param name="progress">The progress manager for the erasure of the current /// stream.</param> protected void EraseStream(StreamInfo info, ProgressManager progress) { //Check that the file exists - we do not want to bother erasing nonexistant files if (!info.Exists) { Logger.Log(S._("The file {0} was not erased as the file does not exist.", info.FileName), LogLevel.Notice); return; } //Get the filesystem provider to handle the secure file erasures IFileSystem fsManager = Host.Instance.FileSystems[ VolumeInfo.FromMountPoint(info.DirectoryName)]; bool isReadOnly = false; try { //Update the task progress IErasureMethod method = EffectiveMethod; //Remove the read-only flag, if it is set. if (isReadOnly = info.IsReadOnly) { info.IsReadOnly = false; } //Define the callback function for progress reporting. ErasureMethodProgressFunction callback = delegate(long lastWritten, long totalData, int currentPass) { if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } progress.Tag = new int[] { currentPass, method.Passes }; progress.Total = totalData; progress.Completed += lastWritten; }; TryEraseStream(fsManager, method, info, callback); //Remove the file. FileInfo fileInfo = info.File; if (fileInfo != null) { fsManager.DeleteFile(fileInfo); } progress.MarkComplete(); } catch (UnauthorizedAccessException) { Logger.Log(S._("The file {0} could not be erased because the file's " + "permissions prevent access to the file.", info.FullName), LogLevel.Error); } catch (SharingViolationException e) { Logger.Log(S._("The file {0} could not be erased because the file is " + "currently in used by another application.", info.FullName), LogLevel.Error); } finally { //Re-set the read-only flag if the file exists (i.e. there was an error) if (isReadOnly && info.Exists && !info.IsReadOnly) { info.IsReadOnly = isReadOnly; } } }
public abstract void EraseFileSystemObject(StreamInfo info, IErasureMethod method, ErasureMethodProgressFunction callback);
public override void EraseOldFileSystemResidentFiles(VolumeInfo volume, DirectoryInfo tempDirectory, IErasureMethod method, FileSystemEntriesEraseProgress callback) { //Squeeze files smaller than one MFT record until the volume and the MFT is full. long MFTRecordSize = NtfsApi.GetMftRecordSegmentSize(volume); long lastFileSize = MFTRecordSize; try { for ( ; ;) { //Open this stream string fileName = GenerateRandomFileName(tempDirectory, 18); FileStream strm = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough); try { //Stretch the file size to use up some of the resident space. strm.SetLength(lastFileSize); //Then run the erase task method.Erase(strm, long.MaxValue, Host.Instance.Prngs.ActivePrng, null); //Call the callback function if one is provided. We'll provide a dummy //value since we really have no idea how much of the MFT we can clean. if (callback != null) { callback((int)(MFTRecordSize - lastFileSize), (int)MFTRecordSize); } } catch (IOException) { if (lastFileSize-- == 0) { break; } } finally { //Close the stream handle strm.Close(); //Then reset the time the file was created. ResetFileTimes(new FileInfo(fileName)); } } } catch (IOException) { //OK, enough squeezing: there isn't enough space to even create a new MFT record. } }
public override void Execute() { //Check for sufficient privileges to run the erasure. if (!Security.IsAdministrator()) { if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= new Version(6, 0)) { Logger.Log(S._("The program does not have the required permissions to erase " + "the disk. Run the program as an administrator and retry the operation."), LogLevel.Error); } else { Logger.Log(S._("The program does not have the required permissions to erase " + "the disk."), LogLevel.Error); } return; } Progress = new SteppedProgressManager(); ProgressManager stepProgress = new ProgressManager(); Progress.Steps.Add(new SteppedProgressManagerStep(stepProgress, 1.0f, ToString())); FileStream stream = null; try { //Overwrite the entire drive IErasureMethod method = EffectiveMethod; if (Volume != null) { stepProgress.Total = Volume.TotalSize; stream = Volume.Open(FileAccess.ReadWrite, FileShare.ReadWrite); } else if (PhysicalDrive != null) { stepProgress.Total = PhysicalDrive.Size; PhysicalDrive.DeleteDriveLayout(); if (PhysicalDrive.Volumes.Count == 1) { //This could be a removable device where Windows sees an oversized floppy. stream = PhysicalDrive.Volumes[0].Open(FileAccess.ReadWrite, FileShare.ReadWrite); } else if (PhysicalDrive.Volumes.Count > 0) { throw new InvalidOperationException(S._("The partition table on the drive " + "could not be erased.")); } else { stream = PhysicalDrive.Open(FileAccess.ReadWrite, FileShare.ReadWrite); } } else { throw new InvalidOperationException(S._("The Drive erasure target requires a " + "volume or physical drive selected for erasure.")); } //Calculate the size of the erasure stepProgress.Total = method.CalculateEraseDataSize(null, stepProgress.Total); //Then run the erase task method.Erase(stream, long.MaxValue, Host.Instance.Prngs.ActivePrng, delegate(long lastWritten, long totalData, int currentPass) { stepProgress.Completed += lastWritten; stepProgress.Tag = new int[] { currentPass, method.Passes }; if (Task.Canceled) { throw new OperationCanceledException(S._("The task was cancelled.")); } } ); } finally { Progress = null; if (stream != null) { stream.Close(); } } }
public virtual bool SupportsMethod(IErasureMethod method) { return(true); }
public abstract void EraseClusterTips(VolumeInfo info, IErasureMethod method, ClusterTipsSearchProgress searchCallback, ClusterTipsEraseProgress eraseCallback);