/// <summary> /// Restores a single backup entry to the file system /// </summary> /// <param name="restoreEntry"> /// The entry to restore /// </param> void RestoreEntry(SkyFloe.Restore.Entry restoreEntry) { // fetch the corresponding backup entry var backupEntry = this.Archive.BackupIndex.FetchEntry(restoreEntry.BackupEntryID); ReportProgress( new ProgressEventArgs() { Operation = "BeginRestoreEntry", BackupSession = backupEntry.Session, BackupEntry = backupEntry, RestoreSession = this.Session, RestoreEntry = restoreEntry } ); var path = IO.Path.Empty; try { // determine whether to restore the entry path = PrepareFile(backupEntry); if (!path.IsEmpty) { // restore or delete the entry if (backupEntry.State != SkyFloe.Backup.EntryState.Deleted) RestoreFile(path, backupEntry, restoreEntry); else if (this.Session.EnableDeletes) IO.FileSystem.Delete(path); this.Session.RestoreLength += restoreEntry.Length; } // the restore was successful or skipped, so commit it to the index restoreEntry.State = SkyFloe.Restore.EntryState.Completed; using (var txn = new TransactionScope()) { this.Archive.RestoreIndex.UpdateEntry(restoreEntry); this.Archive.RestoreIndex.UpdateSession(this.Session); txn.Complete(); } } catch (Exception e) { if (!path.IsEmpty) try { IO.FileSystem.Delete(path); } catch { } switch (ReportError("RestoreEntry", e)) { case ErrorResult.Retry: // the client chose to retry, so leave the entry pending break; case ErrorResult.Fail: // the client chose to ignore the failed entry, // so mark it as failed and continue restoreEntry = this.Archive.RestoreIndex.FetchEntry(restoreEntry.ID); restoreEntry.State = SkyFloe.Restore.EntryState.Failed; this.Archive.RestoreIndex.UpdateEntry(restoreEntry); break; default: // the client chose to abort, so propagate the exception throw; } } if (restoreEntry.State == SkyFloe.Restore.EntryState.Completed) ReportProgress( new ProgressEventArgs() { Operation = "EndRestoreEntry", BackupSession = backupEntry.Session, BackupEntry = backupEntry, RestoreSession = this.Session, RestoreEntry = restoreEntry } ); }
/// <summary> /// Backs up a single file entry to the attached archive /// </summary> /// <param name="entry"> /// The entry to store /// </param> private void BackupEntry(SkyFloe.Backup.Entry entry) { ReportProgress( new ProgressEventArgs() { Operation = "BeginBackupEntry", BackupSession = this.Session, BackupEntry = entry } ); try { using (var input = new IO.StreamStack()) { // create the input stream stack, consisting of the following // . source file // . CRC calculation filter // . optional compressor // . encryptor // . read rate limiter input.Push( IO.FileSystem.Open(entry.Node.GetAbsolutePath()) ); input.Push( new IO.CrcFilter(input.Top) ); if (this.Session.Compress) input.Push( new IO.CompressionStream( input.Top, IO.CompressionMode.Compress ) ); input.Push( new CryptoStream( input.Top, this.Crypto.CreateEncryptor(), CryptoStreamMode.Read ) ); input.Push( this.limiter.CreateStreamFilter(input.Top) ); // send the stream stack to the backup plugin // and record the calculated CRC value in the entry this.backup.Backup(entry, input); entry.Crc32 = input.GetStream<IO.CrcFilter>().Value; } // commit the archive entry to the index // at this point, the entry is locally durable // until the next checkpoint, when it becomes permanent entry.State = SkyFloe.Backup.EntryState.Completed; entry.Blob.Length += entry.Length; this.Session.ActualLength += entry.Length; using (var txn = new TransactionScope()) { this.Archive.BackupIndex.UpdateEntry(entry); this.Archive.BackupIndex.UpdateBlob(entry.Blob); this.Archive.BackupIndex.UpdateSession(this.Session); txn.Complete(); } } catch (Exception e) { switch (ReportError("BackupEntry", e)) { case ErrorResult.Retry: // the client chose to retry, so leave the entry pending break; case ErrorResult.Fail: // the client chose to ignore the failed entry, // so mark it as failed and continue entry = this.Archive.BackupIndex.FetchEntry(entry.ID); entry.State = SkyFloe.Backup.EntryState.Failed; this.Archive.BackupIndex.UpdateEntry(entry); break; default: // the client chose to abort, so propagate the exception throw; } } if (entry.State == SkyFloe.Backup.EntryState.Completed) ReportProgress( new ProgressEventArgs() { Operation = "EndBackupEntry", BackupSession = this.Session, BackupEntry = entry } ); }