public static FileRecord LoadFromFile(FileStream f, DataDrive drive) { FileRecord rec = new FileRecord(); rec.Name = ReadString(f); if (rec.Name.Length == 0) { return(null); } rec.Length = ReadLong(f); rec.Attributes = (FileAttributes)ReadUInt32(f); rec.CreationTime = ReadDateTime(f); rec.LastWriteTime = ReadDateTime(f); rec.StartBlock = ReadUInt32(f); rec.HashCode = new byte[16]; rec.drive = drive; if (f.Read(rec.HashCode, 0, 16) != 16) { return(null); } if (rec.Name[0] == '\\') { rec.Name = rec.Name.TrimStart('\\'); } return(rec); }
private void Initialize(FileInfo info, string name, DataDrive drive) { Name = name; Length = info.Length; Attributes = info.Attributes; CreationTime = info.CreationTime; LastWriteTime = info.LastWriteTime; StartBlock = 0; this.drive = drive; }
public DataDriveViewModel(DataDrive dataDrive, Config config) { this.config = config; DataDrive = dataDrive; DataDrive.PropertyChanged += HandlePropertyChanged; DataDrive.ChangesDetected += HandleChangesDetected; UpdateStatus(); UpdateFileCount(); UpdateAdditionalInfo(); autoScanTimer = new System.Timers.Timer(1000); autoScanTimer.AutoReset = true; autoScanTimer.Elapsed += HandleAutoScanTimer; }
public FileRecord(FileInfo info, string path, DataDrive drive) { Initialize(info, Utils.MakeFullPath(path, info.Name), drive); }
public FileRecord(string filePath, DataDrive drive) { FileInfo fi = new FileInfo(filePath); Initialize(fi, Utils.StripRoot(drive.Root, filePath), drive); }
private void RecoverBlock(DataDrive drive, UInt32 block, ParityBlock parity) { FileRecord r; parity.Load(block); foreach (DataDrive d in drives) if (d != drive) { string error = ""; try { if (d.ReadBlock(block, tempBuf, out r)) { parity.Add(tempBuf); if (r.Modified) error = String.Format("Warning: {0} has been modified. Some recovered files may be corrupt.", r.FullPath); } else if (r != null && !File.Exists(r.FullPath)) error = String.Format("Warning: {0} could not be found. Some recovered files may be corrupt.", r.FullPath); } catch (Exception e) { error = e.Message; // ReadBlock should have constructed a nice error message for us } if (error != "" && errorFiles.Add(error)) FireErrorMessage(error); } }
public static FileRecord LoadFromFile(FileStream f, DataDrive drive) { FileRecord rec = new FileRecord(); rec.Name = ReadString(f); if (rec.Name.Length == 0) return null; rec.Length = ReadLong(f); rec.Attributes = (FileAttributes)ReadUInt32(f); rec.CreationTime = ReadDateTime(f); rec.LastWriteTime = ReadDateTime(f); rec.StartBlock = ReadUInt32(f); rec.HashCode = new byte[16]; rec.drive = drive; if (f.Read(rec.HashCode, 0, 16) != 16) return null; if (rec.Name[0] == '\\') rec.Name = rec.Name.TrimStart('\\'); return rec; }
private bool ValidDrive(DataDrive drive) { foreach (DataDrive d in drives) if (d == drive) return true; return false; }
private void PrintBlockMask(DataDrive d) { BitArray blockMask = d.BlockMask; foreach (bool b in blockMask) Console.Write("{0}", b ? 'X' : '.'); Console.WriteLine(); }
public void Undelete(DataDrive drive, List<string> fileNames) { List<FileRecord> files = new List<FileRecord>(); foreach (FileRecord r in drive.Deletes) if (fileNames.Contains(r.FullPath)) files.Add(r); if (files.Count == 0) { LogFile.Log("No files to undelete."); return; } LogFile.Log("Beginning undelete for {0} file{1}", files.Count, files.Count == 1 ? "" : "s"); cancel = false; recoverTotalBlocks = 0; errorFiles.Clear(); Progress = 0; try { foreach (FileRecord r in files) recoverTotalBlocks += r.LengthInBlocks; recoverBlocks = 0; int errors = 0; int restored = 0; foreach (FileRecord r in files) { if (RecoverFile(r, drive.Root)) { restored++; drive.Deletes.Remove(r); drive.MaybeRemoveAddByName(r.FullPath); } else if (!cancel) errors++; if (cancel) break; else { string statusMsg = String.Format("{0} file{1} restored.", restored, restored == 1 ? "" : "s"); if (errors > 0) statusMsg += " Errors: " + errors; Status = statusMsg; } } } finally { drive.Progress = 0; drive.Status = ""; } }
public void ReloadDrives() { drives.Clear(); try { foreach (Drive d in Config.Drives) { if (File.Exists(Path.Combine(Config.ParityDir, d.Metafile))) Empty = false; DataDrive dataDrive = new DataDrive(d.Path, d.Metafile, Config, env); dataDrive.ErrorMessage += HandleDataDriveErrorMessage; drives.Add(dataDrive); } } catch (Exception e) { drives.Clear(); throw e; } // try to record how many drives in the registry try { Registry.SetValue("HKEY_CURRENT_USER\\Software\\disParity", "dc", Config.Drives.Count, RegistryValueKind.DWord); Registry.SetValue("HKEY_CURRENT_USER\\Software\\disParity", "mpb", MaxParityBlock(), RegistryValueKind.DWord); } catch { } }
/// <summary> /// Remove all files from the given drive from the parity set /// </summary> public void RemoveAllFiles(DataDrive drive) { totalUpdateBlocks = 0; cancel = false; // make a copy of the drive's file table to work on FileRecord[] files = drive.Files.ToArray(); // get total blocks for progress reporting foreach (FileRecord r in files) totalUpdateBlocks += r.LengthInBlocks; currentUpdateBlocks = 0; Progress = 0; drive.Progress = 0; foreach (FileRecord r in files) { RemoveFromParity(r); if (cancel) break; } Progress = 0; drive.Progress = 0; }
/// <summary> /// Recover all files from the given drive to the given location /// </summary> public void Recover(DataDrive drive, string path, out int successes, out int failures) { cancel = false; if (!ValidDrive(drive)) throw new Exception("Invalid drive passed to Recover"); successes = 0; failures = 0; recoverTotalBlocks = 0; errorFiles.Clear(); Progress = 0; foreach (FileRecord f in drive.Files) recoverTotalBlocks += f.LengthInBlocks; recoverBlocks = 0; try { foreach (FileRecord f in drive.Files) if (RecoverFile(f, path)) successes++; else { if (cancel) return; failures++; } } finally { Progress = 0; drive.Progress = 0; drive.Status = ""; } }
public void HashCheck(DataDrive driveToCheck = null) { cancel = false; int files = 0; int failures = 0; int inProgres = 0; long totalBlocksAllDrives = 0; long blocks = 0; if (driveToCheck != null) totalBlocksAllDrives = driveToCheck.TotalFileBlocks; else foreach (DataDrive d in drives) totalBlocksAllDrives += d.TotalFileBlocks; Progress = 0; // If a drive encounters a serious problem during its hash check, this will be set Exception fatalException = null; // Start hashcheck tasks for each drive foreach (DataDrive drive in drives) { if (driveToCheck != null && drive != driveToCheck) continue; DataDrive d = drive; Interlocked.Increment(ref inProgres); Task.Factory.StartNew(() => { try { UInt32 b = 0; UInt32 totalBlocks = d.TotalFileBlocks; byte[] buf = new byte[Parity.BLOCK_SIZE]; LogFile.Log("Starting hashcheck for " + d.Root); d.Progress = 0; using (MD5 hash = MD5.Create()) foreach (FileRecord r in d.Files) { Interlocked.Increment(ref files); // skip zero length files if (r.Length == 0) continue; // make sure file exists if (!File.Exists(r.FullPath)) { FireErrorMessage(r.FullPath + " not found. Skipping hash check for this file."); b += r.LengthInBlocks; Interlocked.Add(ref blocks, r.LengthInBlocks); continue; } // warn if file has been modified if (r.Modified) FireErrorMessage("Warning: " + r.FullPath + " has been modified. Hashcheck will probably fail."); d.Status = "Reading " + r.FullPath; LogFile.Log(d.Status); hash.Initialize(); int read = 0; try { using (FileStream s = new FileStream(r.FullPath, FileMode.Open, FileAccess.Read)) { while (!cancel && ((read = s.Read(buf, 0, Parity.BLOCK_SIZE)) > 0)) { hash.TransformBlock(buf, 0, read, buf, 0); d.Progress = (double)b++ / totalBlocks; Interlocked.Increment(ref blocks); } } } catch (Exception e) { FireErrorMessage("Error reading " + r.FullPath + ": " + e.Message); // progress will be little off after this point, but this is very unlikely anyway so let it be continue; } if (cancel) { d.Status = ""; return; } hash.TransformFinalBlock(buf, 0, 0); if (!Utils.HashCodesMatch(hash.Hash, r.HashCode)) { FireErrorMessage(r.FullPath + " hash check failed"); Interlocked.Increment(ref failures); } } d.Status = ""; if (failures == 0) { if (driveToCheck == null) d.Status = "Hash check complete. No errors found."; LogFile.Log("Hash check of " + d.Root + " complete. No errors found."); } else { if (driveToCheck == null) d.Status = "Hash check complete. Errors: " + failures; LogFile.Log("Hash check of " + d.Root + " complete. Errors: " + failures); } } catch (Exception e) { d.Status = "Hash check failed: " + e.Message; LogFile.Log("Hash check of " + d.Root + " failed: " + e.Message); fatalException = e; // this will halt the hash check of all drives below } finally { d.Progress = 0; Interlocked.Decrement(ref inProgres); } }); } // wait for all hashcheck threads to complete while (inProgres > 0) { if (fatalException != null) { // something serious happened to one of the drives? cancel = true; // stop all the other tasks // CancallableOperation will catch this and log it throw fatalException; } Status = String.Format("Hash check in progress. Files checked: {0} Failures: {1}", files, failures); Progress = (double)blocks / totalBlocksAllDrives; Thread.Sleep(100); } string status; if (driveToCheck != null) status = "Hash check of " + driveToCheck.Root + " complete."; else status = "Hash check of all drives complete."; if (failures == 0) Status = status; else Status = status + " Errors: " + failures; if (driveToCheck != null) LogFile.Log(Status); }
/// <summary> /// Adds a new drive to the parity set to be protected /// </summary> public DataDrive AddDrive(string path) { // to do: Check here that this drive is not alredy in the list! string metaFile = FindAvailableMetafileName(); // make sure there isn't already a file there with this name, if there is, rename it string fullPath = Path.Combine(Config.ParityDir, metaFile); if (File.Exists(fullPath)) File.Move(fullPath, Path.ChangeExtension(fullPath, ".old")); DataDrive newDrive = new DataDrive(path, metaFile, Config, env); newDrive.ErrorMessage += HandleDataDriveErrorMessage; drives.Add(newDrive); // update Config and save Config.Drives.Add(new Drive(path, metaFile)); Config.Save(); return newDrive; }
private DataDriveViewModel AddDrive(DataDrive drive) { DataDriveViewModel vm = new DataDriveViewModel(drive, config); drives.Add(vm); drive.PropertyChanged += HandleDateDrivePropertyChanged; drive.ScanCompleted += HandleScanCompleted; return vm; }
private void FlushTempParity(DataDrive drive, ParityChange change) { bool saveInProgress = true; Task.Factory.StartNew(() => { try { change.Save(); } catch { } finally { saveInProgress = false; } }); while (saveInProgress) { Thread.Sleep(20); drive.Progress = (1.0 - TEMP_FLUSH_PERCENT) + TEMP_FLUSH_PERCENT * change.SaveProgress; } drive.Progress = 0; }
public void RemoveEmptyDrive(DataDrive drive) { if (drive.FileCount > 0) throw new Exception("Attempt to remove non-empty drive"); // find the config entry for this drive Drive driveConfig = Config.Drives.Single(s => s.Path == drive.Root); // delete the meta data file, if any string metaFilePath = Path.Combine(Config.ParityDir, driveConfig.Metafile); if (File.Exists(metaFilePath)) File.Delete(Path.Combine(Config.ParityDir, driveConfig.Metafile)); // remove it from the config and save Config.Drives.Remove(driveConfig); Config.Save(); // finally remove the drive from the parity set drives.Remove(drive); }