} // proc UpdateMetaData protected override void ProcessRecord() { var notify = new CmdletNotify(this); var totalBytes = 0L; // Lese den Index ein var index = new FileIndex(); index.ReadIndex(notify, Path.Combine(Source, "index.txt.gz")); // Suche alle aktiven Archive var archives = new Dictionary <string, List <FileIndexItem> >(StringComparer.OrdinalIgnoreCase); using (var bar = notify.CreateStatus("Wiederherstellen eines Verzeichnisses", $"Wiederherstellen von {Source}...")) { var filter = new FileFilterRules(Filter); // Erzeuge Filter und entferne alle Dateien aus dem Index die dem nicht entsprechen if (filter.IsEmpty) { foreach (var c in index) { AddArchive(archives, c, ref totalBytes); } } else { var remove = new List <FileIndexItem>(); foreach (var c in index) { if (filter.IsFiltered(c.RelativePath)) { AddArchive(archives, c, ref totalBytes); } else { remove.Add(c); } } foreach (var c in remove) { index.RemoveEntry(c); } } // Entpacke die Archvie bar.StartRemaining(); bar.Maximum = totalBytes; foreach (var c in archives) { if (Stuff.IsGZipFile(c.Key) || Stuff.IsNoPackFile(c.Key)) // GZip-Datei { using (var src = Stuff.OpenRead(new FileInfo(Path.Combine(Source, c.Key)), notify, CompressMode.Auto)) { var srcFile = c.Value[0]; var dstFile = new FileInfo(Path.Combine(Target, srcFile.RelativePath)); using (var dst = Override ? Stuff.OpenWrite(dstFile, notify) : Stuff.OpenCreate(dstFile, notify)) { dst.SetLength(srcFile.Length); Stuff.CopyRawBytes(bar, srcFile.RelativePath, srcFile.Length, src, dst); } // Aktualisiere die Attribute UpdateMetaData(notify, srcFile, dstFile); } } else // zip-Datei { using (var zipStream = Stuff.OpenRead(new FileInfo(Path.Combine(Source, c.Key)), notify, CompressMode.Stored)) using (var zip = new ZipInputStream(zipStream)) { var srcEntry = zip.GetNextEntry(); while (srcEntry != null) { // Suche den passenden Index var srcIndex = c.Value.Find(c2 => String.Compare(srcEntry.Name, ZipEntry.CleanName(c2.RelativePath), StringComparison.OrdinalIgnoreCase) == 0); if (srcIndex != null) { var dstFile = new FileInfo(Path.Combine(Target, srcIndex.RelativePath)); using (var dst = Override ? Stuff.OpenWrite(dstFile, notify) : Stuff.OpenCreate(dstFile, notify)) { dst.SetLength(srcIndex.Length); Stuff.CopyRawBytes(bar, srcIndex.RelativePath, srcIndex.Length, zip, dst); } // Aktualisiere die Attribute UpdateMetaData(notify, srcIndex, dstFile); } else { zip.CloseEntry(); } // Schließe den Eintrag ab srcEntry = zip.GetNextEntry(); } } } } } } // proc ProcessRecord
protected override void ProcessRecord() { const string zipArchivePlaceHolder = "#"; using (var bar = Notify.CreateStatus("Erzeuge Backup", $"Sicherung von {Source}...")) { var totalBytes = 0L; //var position = 0L; var itemsModified = 0; var itemsUnmodified = 0; var itemsZipped = 0; var archiveUsed = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase); // Lade Index-Datei bar.StatusDescription = "Lese Index..."; var targetPath = new DirectoryInfo(Target); var targetIndex = Path.Combine(targetPath.FullName, "index.txt.gz"); var index = new FileIndex(); if (String.IsNullOrEmpty(ShadowIndex)) // Kein lokaler Index, also lade dem vom Target { index.ReadIndex(Notify, targetIndex); } else { index.ReadIndex(Notify, ShadowIndex); } // Gleiche die Daten ab und erzeuge die Statistik bar.StatusDescription = "Vergleiche Dateien mit Index..."; var swFileStopWatch = Stopwatch.StartNew(); var files = new FileList(Notify, new DirectoryInfo(Source), Excludes); foreach (var c in files) { var indexItem = index.UpdateFile(c); var tmp = 0; // Gib einen zwischen Bericht if (swFileStopWatch.ElapsedMilliseconds > 500) { bar.StatusDescription = $"Vergleiche {c.RelativePath} mit Index..."; swFileStopWatch = Stopwatch.StartNew(); } switch (indexItem.State) { case FileIndexState.Modified: // Erzeuge den Eintrag im Index if (c.FileInfo.Length < ZipArchiveBorder) { itemsZipped++; indexItem.ArchiveName = zipArchivePlaceHolder; } else { if (String.IsNullOrEmpty(indexItem.ArchiveName)) { indexItem.ArchiveName = Guid.NewGuid().ToString("N") + Path.GetExtension(indexItem.RelativePath) + (ZipFile(indexItem.RelativePath) ? ".gz" : ".nopack"); } } // Statistik für den Progress totalBytes += c.FileInfo.Length; itemsModified++; // Erhöhe den Zugriff if (archiveUsed.TryGetValue(indexItem.ArchiveName, out tmp)) { archiveUsed[indexItem.ArchiveName] = tmp + 1; } break; case FileIndexState.Unmodified: // Prüfe die existens den Archives if (Force || (String.IsNullOrEmpty(ShadowIndex) && !File.Exists(Path.Combine(targetPath.FullName, indexItem.ArchiveName)))) { indexItem.Update(c.FileInfo); goto case FileIndexState.Modified; } itemsUnmodified++; // Erhöhe den Zugriff if (archiveUsed.TryGetValue(indexItem.ArchiveName, out tmp)) { archiveUsed[indexItem.ArchiveName] = tmp + 1; } break; case FileIndexState.None: if (archiveUsed.ContainsKey(indexItem.ArchiveName)) { archiveUsed[indexItem.ArchiveName] = 0; } break; } } // Schreibe das neue Archiv if (itemsModified > 0) { string currentArchiveName = null; FileWrite zipStream = null; ZipOutputStream zip = null; try { bar.StartRemaining(); bar.Maximum = totalBytes; var removeItems = new List <FileIndexItem>(); foreach (var c in index) { switch (c.State) { case FileIndexState.Modified: // Kopiere die Datei using (var src = Stuff.OpenRead(new FileInfo(Path.Combine(Source, c.RelativePath)), Notify, allowEmpty: true)) { if (c.ArchiveName == zipArchivePlaceHolder) { // Schließe das Archiv, wenn genug Inhalt if (zipStream != null && zipStream.Stream.Position > 512 << 20) { zipStream.Commit(); CloseZipStream(zipStream, zip); currentArchiveName = null; } // Erzeuge das Archiv if (currentArchiveName == null) { currentArchiveName = Guid.NewGuid().ToString("N") + ".zip"; CreateZipStream(targetPath, currentArchiveName, out zipStream, out zip); } // Kopiere die Daten ZipFileItem(Notify, bar, src, zip, c); c.ArchiveName = currentArchiveName; } else { GZipFileItem(Notify, bar, src, targetPath, c); } } break; case FileIndexState.None: // Lösche den Index removeItems.Remove(c); break; } } // Entferne die Einträge aus dem Index foreach (var c in removeItems) { index.RemoveEntry(c); } if (zipStream != null) { zipStream.Commit(); } } finally { CloseZipStream(zipStream, zip); } // Schreibe den Index bar.StopRemaining(); bar.StatusDescription = "Schreibe Index..."; if (!String.IsNullOrEmpty(ShadowIndex)) { index.WriteIndex(Notify, ShadowIndex); } index.WriteIndex(Notify, targetIndex); // Lösche ungenutzte Archive if (String.IsNullOrEmpty(ShadowIndex)) { foreach (var c in archiveUsed) { if (c.Value == 0) { var file = new FileInfo(Path.Combine(targetPath.FullName, c.Key)); bar.StatusDescription = $"Nicht mehr benötigtes Archiv '{c.Key}'..."; Notify.SafeIO(file.Delete, bar.StatusDescription); } } } else // Erzeuge nur eine Löschdatei { bar.StatusDescription = "Nicht mehr benötigte Archive werden gelöscht..."; using (var sw = new StreamWriter(Stuff.OpenWrite(new FileInfo(Path.Combine(targetPath.FullName, "index_rm.txt")), Notify, CompressMode.Stored))) { sw.BaseStream.Position = sw.BaseStream.Length; foreach (var c in archiveUsed) { if (c.Value == 0) { sw.WriteLine(c.Key); } } } } } } } // proc ProcessRecord