/// <summary> /// XORs the data from the given file with the parity data. This either adds the file to /// parity or removes it from parity if it was already there. If checkHash is true, /// it verifies the file's hash matches the hash on record before commiting the parity. /// If false, it updates the file's hash on record. /// </summary> private bool XORFileWithParity(FileRecord r, bool checkHash) { if (!File.Exists(r.FullPath)) return false; if (r.Length == 0) return true; using (ParityChange change = new ParityChange(parity, Config, r.StartBlock, r.LengthInBlocks)) { byte[] data = new byte[Parity.BLOCK_SIZE]; MD5 hash = MD5.Create(); hash.Initialize(); UInt32 endBlock = r.StartBlock + r.LengthInBlocks; UInt32 totalProgresBlocks = r.LengthInBlocks + (UInt32)(TEMP_FLUSH_PERCENT * r.LengthInBlocks); FileStream f; try { f = new FileStream(r.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (Exception e) { FireErrorMessage(String.Format("Error opening {0}: {1}", r.FullPath, e.Message)); return false; } try { for (UInt32 b = r.StartBlock; b < endBlock; b++) { Int32 bytesRead; try { bytesRead = f.Read(data, 0, Parity.BLOCK_SIZE); } catch (Exception e) { FireErrorMessage(String.Format("Error reading {0}: {1}", r.FullPath, e.Message)); return false; } if (b == (endBlock - 1)) hash.TransformFinalBlock(data, 0, bytesRead); else hash.TransformBlock(data, 0, bytesRead, data, 0); while (bytesRead < Parity.BLOCK_SIZE) data[bytesRead++] = 0; change.Reset(true); change.AddData(data); change.Write(); currentUpdateBlocks++; r.Drive.Progress = (double)(b - r.StartBlock) / totalProgresBlocks; Progress = (double)currentUpdateBlocks / totalUpdateBlocks; if (cancel) return false; } } catch (Exception e) { env.LogCrash(e); FireErrorMessage(String.Format("Unexpected error while processing {0}: {1}", r.FullPath, e.Message)); return false; } finally { f.Dispose(); } if (checkHash) { if (!Utils.HashCodesMatch(hash.Hash, r.HashCode)) { LogFile.Log("Tried to remove existing file but hash codes don't match."); return false; } } else r.HashCode = hash.Hash; FlushTempParity(r.Drive, change); // commit the parity change to disk } r.Drive.Progress = 0; return true; }
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; }
private bool RemoveFromParity(FileRecord r) { // make a backup copy of the meta file first. If this fails, we know we won't be able to complete the remove. if (!r.Drive.BackupMetaFile()) { FireErrorMessage("Error removing " + r.FullPath + ": could not back up " + r.Drive.MetaFile); return false; } bool success = false; try { if (r.Length > 0) { string fullPath = r.FullPath; UInt32 startBlock = r.StartBlock; UInt32 endBlock = startBlock + r.LengthInBlocks; if (LogFile.Verbose) LogFile.Log("Removing {0} from blocks {1} to {2}...", fullPath, startBlock, endBlock - 1); else LogFile.Log("Removing {0}...", fullPath); r.Drive.Status = "Removing " + fullPath; // Optimization: if the file still exists and is unmodified, we can remove it much faster this way if (!r.Modified && XORFileWithParity(r, true)) { r.Drive.RemoveFile(r); r.Drive.SaveFileList(); success = true; // so finally() clause doesn't try to restore backup return true; } UInt32 totalProgresBlocks = r.LengthInBlocks + (UInt32)(TEMP_FLUSH_PERCENT * r.LengthInBlocks); // Recalulate parity from scratch for all blocks that contained the deleted file's data. using (ParityChange change = new ParityChange(parity, Config, startBlock, r.LengthInBlocks)) try { byte[] data = new byte[Parity.BLOCK_SIZE]; for (UInt32 b = startBlock; b < endBlock; b++) { change.Reset(false); foreach (DataDrive d in drives) { if (d == r.Drive) continue; // Note it's possible that this file may also have been deleted. That's OK, ReadFileData // returns false and we don't try to add the deleted file to the parity. FileRecord f; try { if (d.ReadBlock(b, data, out f)) change.AddData(data); } catch (Exception e) { FireErrorMessage(e.Message); return false; } } change.Write(); currentUpdateBlocks++; r.Drive.Progress = (double)(b - startBlock) / totalProgresBlocks; Progress = (double)currentUpdateBlocks / totalUpdateBlocks; if (cancel) return false; } FlushTempParity(r.Drive, change); } catch (Exception e) { env.LogCrash(e); FireErrorMessage(String.Format("Error removing {0}: {1}", r.FullPath, e.Message)); return false; } } r.Drive.RemoveFile(r); r.Drive.SaveFileList(); success = true; } finally { if (!success) r.Drive.RestoreMetaFile(); // restore the backup filesX.dat created by BackupMetaFile() } return true; }