2009-01-14 17:29:30 +00:00
#region Disclaimer / License
2011-05-09 19:52:28 +00:00
// Copyright (C) 2011, Kenneth Skovhede
2009-01-14 17:29:30 +00:00
// http://www.hexad.dk, opensource@hexad.dk
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System ;
using System.Collections.Generic ;
using System.Text ;
2011-06-17 13:31:28 +00:00
using Duplicati.Library.Utility ;
2009-01-14 17:29:30 +00:00
namespace Duplicati.Library.Main
{
2010-09-26 20:43:16 +00:00
/// <summary>
/// The operations that Duplicati can report
/// </summary>
2009-02-15 23:27:14 +00:00
public enum DuplicatiOperation
2009-01-14 17:29:30 +00:00
{
2010-09-26 20:43:16 +00:00
/// <summary>
/// Indicates that the operations is a full or incremental backup
/// </summary>
2009-02-15 23:27:14 +00:00
Backup ,
2010-09-26 20:43:16 +00:00
/// <summary>
/// Indicates that the operation is a restore type operation
/// </summary>
2009-02-15 23:27:14 +00:00
Restore ,
2010-09-26 20:43:16 +00:00
/// <summary>
/// Indicates that the operation is a list type operation
/// </summary>
2009-02-15 23:27:14 +00:00
List ,
2010-09-26 20:43:16 +00:00
/// <summary>
/// Indicates that the operation is a delete type operation
/// </summary>
2009-02-15 23:27:14 +00:00
Remove
2010-11-21 17:53:00 +00:00
}
2009-02-15 23:27:14 +00:00
2010-09-26 20:43:16 +00:00
/// <summary>
/// The actual operation that Duplicati supports
/// </summary>
public enum DuplicatiOperationMode
{
/// <summary>
/// A backup operation, either full or incremental
/// </summary>
Backup ,
/// <summary>
/// A full backup
/// </summary>
BackupFull ,
/// <summary>
/// An incremental backup
/// </summary>
BackupIncremental ,
/// <summary>
/// A restore operation
/// </summary>
Restore ,
/// <summary>
/// A restore operation for control files
/// </summary>
RestoreControlfiles ,
/// <summary>
/// A backend file listing
/// </summary>
List ,
/// <summary>
/// A list of backup chains found on the backend
/// </summary>
GetBackupSets ,
/// <summary>
/// A list of files found in a specific backup set, produced by summing through incremental signature files
/// </summary>
ListCurrentFiles ,
/// <summary>
/// A list of the source folders found in a specific backup set
/// </summary>
ListSourceFolders ,
/// <summary>
/// A list of files found in a specific backup set, only shows content from the single backup set and not the entire chain
/// </summary>
ListActualSignatureFiles ,
/// <summary>
/// A delete operation performed by looking at the number of existing full backups
/// </summary>
DeleteAllButNFull ,
/// <summary>
2011-10-19 05:50:10 +00:00
/// A delete operation performed by looking at the number of existing backups
/// </summary>
DeleteAllButN ,
/// <summary>
2010-09-26 20:43:16 +00:00
/// A delete operation performed by looking at the age of existing backups
/// </summary>
DeleteOlderThan ,
/// <summary>
/// A cleanup operation that removes orphan files
/// </summary>
2010-10-23 11:40:43 +00:00
CleanUp ,
/// <summary>
/// A request to create the underlying folder
/// </summary>
2010-10-23 13:10:54 +00:00
CreateFolder ,
/// <summary>
/// A search for files in signature files
/// </summary>
2011-03-10 18:26:38 +00:00
FindLastFileVersion ,
/// <summary>
/// Verifies the hashes and backup chain
/// </summary>
Verify
}
/// <summary>
/// An enum that describes the level of verification done by the verify command
/// </summary>
public enum VerificationLevel
{
/// <summary>
/// Just verify the manifest chain
/// </summary>
Manifest ,
/// <summary>
/// Verify the manifest chain and all signature files
/// </summary>
Signature ,
/// <summary>
/// Verify everything, including the content files, requires download of all files
/// </summary>
Full ,
2010-09-26 20:43:16 +00:00
}
/// <summary>
/// A delegate for reporting progress from within the Duplicati module
/// </summary>
/// <param name="caller">The instance that is running</param>
/// <param name="operation">The overall operation type</param>
/// <param name="specificoperation">A more specific type of operation</param>
/// <param name="progress">The current overall progress of the operation</param>
/// <param name="subprogress">The progress of a transfer</param>
/// <param name="message">A message describing the current operation</param>
/// <param name="submessage">A message describing the current transfer operation</param>
public delegate void OperationProgressEvent ( Interface caller , DuplicatiOperation operation , DuplicatiOperationMode specificoperation , int progress , int subprogress , string message , string submessage );
2009-02-15 23:27:14 +00:00
2012-05-14 18:48:21 +00:00
/// <summary>
/// A delegate used to report metadata about the current backup, obtained while running a normal operation
/// </summary>
/// <param name="metadata">A table with various metadata informations</param>
public delegate void MetadataReportDelegate ( IDictionary < string , string > metadata );
2010-01-03 22:46:58 +00:00
public class Interface : IDisposable , LiveControl . ILiveControl
2009-02-15 23:27:14 +00:00
{
2010-08-25 20:59:47 +00:00
/// <summary>
/// The amount of progressbar allocated for reading incremental data
/// </summary>
private const double INCREMENAL_COST = 0.10 ;
/// <summary>
/// The amount of progressbar allocated for uploading async volumes
/// </summary>
private const double ASYNC_RESERVED = 0.10 ;
2010-01-16 16:18:59 +00:00
2012-08-15 18:33:46 +00:00
/// <summary>
/// The backend url
/// </summary>
2009-02-15 23:27:14 +00:00
private string m_backend ;
2012-08-15 18:33:46 +00:00
/// <summary>
/// The parsed type-safe version of the commandline options
/// </summary>
2009-03-22 22:55:04 +00:00
private Options m_options ;
2009-02-15 23:27:14 +00:00
2012-08-15 18:33:46 +00:00
/// <summary>
/// The restult of an operation, used to report via IGenericCallbackModule
/// </summary>
private object m_result ;
2010-08-25 20:59:47 +00:00
/// <summary>
/// The amount of progressbar allocated for reading incremental data
/// </summary>
2010-01-16 16:18:59 +00:00
private double m_incrementalFraction = INCREMENAL_COST ;
2010-08-25 20:59:47 +00:00
/// <summary>
/// The amount of progressbar allocated for uploading the remaining volumes in asynchronous mode
/// </summary>
private double m_asyncReserved = 0.0 ;
/// <summary>
/// The current overall progress without taking the reserved amounts into account
/// </summary>
2009-03-11 20:56:01 +00:00
private double m_progress = 0.0 ;
2010-09-26 20:43:16 +00:00
/// <summary>
/// The number of restore patches
/// </summary>
private int m_restorePatches = 0 ;
2012-04-11 19:41:13 +00:00
/// <summary>
/// Cache of the last progress message
/// </summary>
2010-01-16 16:18:59 +00:00
private string m_lastProgressMessage = "" ;
2012-04-11 19:41:13 +00:00
/// <summary>
/// A flag indicating if logging has been set, used to dispose the logging
/// </summary>
private bool m_hasSetLogging = false ;
2010-01-16 16:18:59 +00:00
2010-08-25 20:59:47 +00:00
/// <summary>
/// A flag toggling if the upload progress is reported,
/// used to prevent showing the progress bar when performing
/// ansynchronous uploads
/// </summary>
private bool m_allowUploadProgress = true ;
/// <summary>
/// When allowing the upload progress to be reported,
/// there can be a flicker because the upload progresses,
/// before the flag is disabled again. This value
/// is set to DateTime.Now.AddSeconds(1) before
/// entering a potentially blocking operation, which
/// allows the progress to be reported if the call blocks,
/// but prevents flicker from non-blocking calls.
/// </summary>
private DateTime m_allowUploadProgressAfter = DateTime . Now ;
2009-02-15 23:27:14 +00:00
public event OperationProgressEvent OperationStarted ;
public event OperationProgressEvent OperationCompleted ;
public event OperationProgressEvent OperationProgress ;
public event OperationProgressEvent OperationError ;
2012-05-14 18:48:21 +00:00
public event MetadataReportDelegate MetadataReport ;
2009-02-15 23:27:14 +00:00
2010-01-03 22:46:58 +00:00
/// <summary>
/// The live control interface
/// </summary>
private LiveControl . LiveControl m_liveControl ;
2011-06-17 13:31:28 +00:00
/// <summary>
/// This gets called whenever execution of an operation is started or stopped; it currently handles the AllowSleep option
/// </summary>
/// <param name="isRunning">Flag indicating execution state</param>
private void OperationRunning ( bool isRunning )
{
if ( m_options != null && ! m_options . AllowSleep && ! Duplicati . Library . Utility . Utility . IsClientLinux )
try
{
Win32 . SetThreadExecutionState ( Win32 . EXECUTION_STATE . ES_CONTINUOUS | ( isRunning ? Win32 . EXECUTION_STATE . ES_SYSTEM_REQUIRED : 0 ));
}
catch { } //TODO: Report this somehow
}
2010-09-26 20:43:16 +00:00
/// <summary>
/// Returns the current overall operation mode based on the actual operation mode
/// </summary>
/// <returns>The overall operation type</returns>
private DuplicatiOperation GetOperationType ()
{
switch ( m_options . MainAction )
{
case DuplicatiOperationMode . Backup :
case DuplicatiOperationMode . BackupFull :
case DuplicatiOperationMode . BackupIncremental :
return DuplicatiOperation . Backup ;
case DuplicatiOperationMode . Restore :
case DuplicatiOperationMode . RestoreControlfiles :
return DuplicatiOperation . Restore ;
case DuplicatiOperationMode . List :
case DuplicatiOperationMode . GetBackupSets :
case DuplicatiOperationMode . ListCurrentFiles :
case DuplicatiOperationMode . ListSourceFolders :
case DuplicatiOperationMode . ListActualSignatureFiles :
2011-03-11 20:12:53 +00:00
case DuplicatiOperationMode . FindLastFileVersion :
case DuplicatiOperationMode . Verify :
2010-09-26 20:43:16 +00:00
return DuplicatiOperation . List ;
case DuplicatiOperationMode . DeleteAllButNFull :
case DuplicatiOperationMode . DeleteOlderThan :
case DuplicatiOperationMode . CleanUp :
return DuplicatiOperation . Remove ;
2011-03-11 20:12:53 +00:00
2010-09-26 20:43:16 +00:00
default :
throw new Exception ( string . Format ( Strings . Interface . UnexpectedOperationTypeError , m_options . MainAction ));
}
}
/// <summary>
/// Constructs a new interface for performing backup and restore operations
/// </summary>
/// <param name="backend">The url for the backend to use</param>
/// <param name="options">All required options</param>
2009-02-15 23:27:14 +00:00
public Interface ( string backend , Dictionary < string , string > options )
{
m_backend = backend ;
2009-03-22 22:55:04 +00:00
m_options = new Options ( options );
2010-01-03 22:46:58 +00:00
m_liveControl = new LiveControl . LiveControl ( System . Threading . Thread . CurrentThread , m_options );
2010-01-16 16:18:59 +00:00
OperationProgress += new OperationProgressEvent ( Interface_OperationProgress );
}
/// <summary>
/// Event handler for the OperationProgres, used to store the last status message
/// </summary>
2010-09-26 20:43:16 +00:00
private void Interface_OperationProgress ( Interface caller , DuplicatiOperation operation , DuplicatiOperationMode specificoperation , int progress , int subprogress , string message , string submessage )
2010-01-16 16:18:59 +00:00
{
m_lastProgressMessage = message ;
2009-03-26 19:39:50 +00:00
}
2010-09-26 20:43:16 +00:00
/// <summary>
/// Internal helper to pause and stop when requested
/// </summary>
2010-01-03 22:46:58 +00:00
private void CheckLiveControl ()
2009-03-26 19:39:50 +00:00
{
2011-06-17 13:31:28 +00:00
if ( m_liveControl . IsPauseRequested )
{
OperationRunning ( false );
try { m_liveControl . PauseIfRequested (); }
finally { OperationRunning ( true ); }
}
2010-01-03 22:46:58 +00:00
if ( m_liveControl . IsStopRequested )
throw new LiveControl . ExecutionStoppedException ();
2009-02-15 23:27:14 +00:00
}
2010-05-12 08:41:54 +00:00
public string Backup ( string [] sources )
2009-01-14 17:29:30 +00:00
{
2010-09-26 20:43:16 +00:00
BackupStatistics bs = new BackupStatistics ( DuplicatiOperationMode . Backup );
2012-08-15 18:33:46 +00:00
SetupCommonOptions ( bs , ref sources );
2009-01-17 15:27:51 +00:00
2009-03-26 19:39:50 +00:00
BackendWrapper backend = null ;
2011-03-10 18:26:38 +00:00
VerificationFile verification = null ;
2012-08-15 18:33:46 +00:00
using ( new Logging . Timer ( "Backup from " + string . Join ( ";" , sources ) + " to " + m_backend ))
{
try
{
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . ManifestsMustBeReadOnBackups );
if ( m_options . SkipFileHashChecks )
throw new Exception ( Strings . Interface . CannotSkipHashChecksOnBackup );
2009-03-30 20:17:54 +00:00
2012-08-15 18:33:46 +00:00
if ( sources == null || sources . Length == 0 )
throw new Exception ( Strings . Interface . NoSourceFoldersError );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
//Make sure they all have the same format and exist
for ( int i = 0 ; i < sources . Length ; i ++)
{
sources [ i ] = Utility . Utility . AppendDirSeparator ( System . IO . Path . GetFullPath ( sources [ i ]));
2012-05-17 08:07:41 +00:00
2012-08-15 18:33:46 +00:00
if (! System . IO . Directory . Exists ( sources [ i ]))
throw new System . IO . IOException ( String . Format ( Strings . Interface . SourceFolderIsMissingError , sources [ i ]));
}
2010-08-12 18:56:04 +00:00
2012-08-15 18:33:46 +00:00
//Sanity check for duplicate folders and multiple inclusions of the same folder
for ( int i = 0 ; i < sources . Length - 1 ; i ++)
{
for ( int j = i + 1 ; j < sources . Length ; j ++)
if ( sources [ i ]. Equals ( sources [ j ], Utility . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
throw new Exception ( string . Format ( Strings . Interface . SourceDirIsIncludedMultipleTimesError , sources [ i ]));
else if ( sources [ i ]. StartsWith ( sources [ j ], Utility . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
throw new Exception ( string . Format ( Strings . Interface . SourceDirsAreRelatedError , sources [ i ], sources [ j ]));
}
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
if ( m_options . AsynchronousUpload )
{
m_asyncReserved = ASYNC_RESERVED ;
m_allowUploadProgress = false ;
}
2011-03-10 18:26:38 +00:00
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2010-09-26 20:43:16 +00:00
OperationStarted ( this , DuplicatiOperation . Backup , bs . OperationMode , - 1 , - 1 , Strings . Interface . StatusLoadingFilelist , "" );
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , - 1 , - 1 , Strings . Interface . StatusLoadingFilelist , "" );
2009-03-11 20:56:01 +00:00
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-01-25 19:33:36 +00:00
2009-03-22 22:55:04 +00:00
bool full = m_options . Full ;
2012-03-17 19:45:09 +00:00
if ( full )
bs . SetTypeReason ( string . Format ( Strings . Interface . FullBecauseFlagWasSet , "full" ));
2009-02-10 23:29:01 +00:00
2009-03-26 19:39:50 +00:00
backend = new BackendWrapper ( bs , m_backend , m_options );
2010-01-16 16:18:59 +00:00
backend . ProgressEvent += new Duplicati . Library . Main . RSync . RSyncDir . ProgressEventDelegate ( BackupTransfer_ProgressEvent );
2010-08-25 20:59:47 +00:00
backend . AsyncItemProcessedEvent += new EventHandler ( backend_AsyncItemProcessedEvent );
2009-01-14 17:29:30 +00:00
2009-03-11 20:56:01 +00:00
m_progress = 0.0 ;
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusReadingIncrementals , "" );
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-03-11 20:56:01 +00:00
2012-05-14 21:13:08 +00:00
List < ManifestEntry > backupsets ;
2009-03-26 19:39:50 +00:00
2012-05-14 21:13:08 +00:00
if ( full )
{
//This will create the target folder
2012-05-15 17:59:40 +00:00
List < Duplicati . Library . Interface . IFileEntry > tmp = backend . List ( false );
//If it is possible to parse the chain, we extract some metadata
try { backend . ExtractMetadataFromList ( tmp ); }
catch { }
2012-05-14 21:20:04 +00:00
backupsets = new List < ManifestEntry >();
2012-05-14 21:13:08 +00:00
}
else
{
//This will list all files on the backend and create the target folder
backupsets = backend . GetBackupSets ();
}
if ( backupsets . Count == 0 )
2011-03-10 18:26:38 +00:00
{
2012-03-17 19:45:09 +00:00
if (! full )
bs . SetTypeReason ( Strings . Interface . FullBecauseBackendIsEmpty );
2009-01-25 19:33:36 +00:00
full = true ;
2011-03-10 18:26:38 +00:00
}
2010-09-30 12:39:52 +00:00
else
{
//A prioir backup exists, extract the compression and encryption modules used in the most recent entry
string compression = null ;
string encryption = null ;
for ( int i = backupsets . Count - 1 ; compression == null && i >= 0 ; i --)
{
for ( int j = backupsets [ i ]. Incrementals . Count - 1 ; compression == null && j >= 0 ; j --)
for ( int k = backupsets [ i ]. Incrementals [ j ]. Volumes . Count - 1 ; compression == null && k >= 0 ; k --)
{
compression = backupsets [ i ]. Incrementals [ j ]. Volumes [ k ]. Key . Compression ;
encryption = backupsets [ i ]. Incrementals [ j ]. Volumes [ k ]. Key . EncryptionMode ;
2011-03-10 18:26:38 +00:00
if ( compression != null )
break ;
2010-09-30 12:39:52 +00:00
}
for ( int k = backupsets [ i ]. Volumes . Count - 1 ; compression == null && k >= 0 ; k --)
{
compression = backupsets [ i ]. Volumes [ k ]. Key . Compression ;
encryption = backupsets [ i ]. Volumes [ k ]. Key . EncryptionMode ;
2011-03-10 18:26:38 +00:00
if ( compression != null )
break ;
2010-09-30 12:39:52 +00:00
}
}
if ( compression != null )
{
m_options . SetEncryptionModuleDefault ( encryption );
m_options . SetCompressionModuleDefault ( compression );
}
}
2009-03-30 20:17:54 +00:00
2012-08-07 17:42:09 +00:00
bool compressionFound = false ;
foreach ( string s in DynamicLoader . CompressionLoader . Keys )
if ( string . Equals ( s , m_options . CompressionModule , StringComparison . InvariantCultureIgnoreCase ))
{
compressionFound = true ;
break ;
}
if (! compressionFound )
throw new Exception ( string . Format ( Strings . Interface . CompressionModuleNotFoundError , m_options . CompressionModule , string . Join ( ", " , DynamicLoader . CompressionLoader . Keys )));
if (! m_options . NoEncryption )
{
bool encryptionFound = false ;
foreach ( string s in DynamicLoader . EncryptionLoader . Keys )
if ( string . Equals ( s , m_options . EncryptionModule , StringComparison . InvariantCultureIgnoreCase ))
{
encryptionFound = true ;
break ;
}
if (! encryptionFound )
throw new Exception ( string . Format ( Strings . Interface . EncryptionModuleNotFoundError , m_options . EncryptionModule , string . Join ( ", " , DynamicLoader . EncryptionLoader . Keys )));
}
2012-03-17 19:45:09 +00:00
string fullCriteria1 = null ;
string fullCriteria2 = null ;
2009-03-22 22:55:04 +00:00
if (! full )
2012-03-17 19:45:09 +00:00
{
2009-03-26 19:39:50 +00:00
full = DateTime . Now > m_options . FullIfOlderThan ( backupsets [ backupsets . Count - 1 ]. Time );
2012-03-17 19:45:09 +00:00
if ( full )
bs . SetTypeReason ( string . Format ( Strings . Interface . FullBecauseLastFullIsFrom , backupsets [ backupsets . Count - 1 ]. Time , m_options . FullIfOlderThanValue ));
2012-03-20 19:44:41 +00:00
else if (! string . IsNullOrEmpty ( m_options . FullIfOlderThanValue ))
2012-03-17 19:45:09 +00:00
fullCriteria1 = string . Format ( Strings . Interface . IncrementalBecauseLastFullIsFrom , backupsets [ backupsets . Count - 1 ]. Time , m_options . FullIfOlderThanValue );
}
if (! full && m_options . FullIfMoreThanNIncrementals > 0 )
{
full = backupsets [ backupsets . Count - 1 ]. Incrementals . Count >= m_options . FullIfMoreThanNIncrementals ;
if ( full )
bs . SetTypeReason ( string . Format ( Strings . Interface . FullBecauseThereAreNIncrementals , backupsets [ backupsets . Count - 1 ]. Incrementals . Count , m_options . FullIfMoreThanNIncrementals ));
else
fullCriteria2 = string . Format ( Strings . Interface . IncrementalBecauseThereAreNIncrementals , backupsets [ backupsets . Count - 1 ]. Incrementals . Count , m_options . FullIfMoreThanNIncrementals );
}
2010-11-21 19:44:35 +00:00
bs . Full = full ;
2012-03-17 19:45:09 +00:00
if (! full )
{
if ( fullCriteria1 == null && fullCriteria2 == null )
bs . SetTypeReason ( Strings . Interface . IncrementalBecauseNoFlagsWereSet );
else if ( fullCriteria2 == null )
bs . SetTypeReason ( fullCriteria1 );
else if ( fullCriteria1 == null )
bs . SetTypeReason ( fullCriteria2 );
else
bs . SetTypeReason ( fullCriteria1 + ". " + fullCriteria2 );
}
2009-01-14 17:29:30 +00:00
2009-02-14 17:30:52 +00:00
List < string > controlfiles = new List < string >();
2009-03-22 22:55:04 +00:00
if (! string . IsNullOrEmpty ( m_options . SignatureControlFiles ))
controlfiles . AddRange ( m_options . SignatureControlFiles . Split ( System . IO . Path . PathSeparator ));
2009-02-14 17:30:52 +00:00
2010-05-12 08:41:54 +00:00
int vol = 0 ;
long totalsize = 0 ;
Manifestfile manifest = new Manifestfile ();
2010-11-21 11:08:15 +00:00
using ( Utility . TempFolder tempfolder = new Duplicati . Library . Utility . TempFolder ())
2009-01-14 17:29:30 +00:00
{
2012-08-07 17:42:09 +00:00
List < KeyValuePair < ManifestEntry , CompressionWrapper >> patches = new List < KeyValuePair < ManifestEntry , CompressionWrapper >>();
2009-01-25 19:33:36 +00:00
if (! full )
2009-01-14 17:29:30 +00:00
{
2010-01-16 16:18:59 +00:00
m_incrementalFraction = INCREMENAL_COST ;
2010-05-12 08:41:54 +00:00
List < ManifestEntry > entries = new List < ManifestEntry >();
2009-03-26 19:39:50 +00:00
entries . Add ( backupsets [ backupsets . Count - 1 ]);
entries . AddRange ( backupsets [ backupsets . Count - 1 ]. Incrementals );
2009-03-09 21:09:27 +00:00
2010-09-01 21:04:18 +00:00
//Check before we start the download
CheckLiveControl ();
2011-03-10 18:26:38 +00:00
VerifyBackupChainWithFiles ( backend , entries [ entries . Count - 1 ]);
if ( m_options . CreateVerificationFile )
verification = new VerificationFile ( entries , backend . FilenameStrategy );
2011-03-12 10:03:54 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusReadingIncrementals , "" );
2011-03-10 18:26:38 +00:00
patches = FindPatches ( backend , entries , tempfolder , false , bs );
2010-05-12 08:41:54 +00:00
2010-09-01 21:04:18 +00:00
//Check before we start the download
CheckLiveControl ();
2010-09-30 12:39:52 +00:00
Manifestfile latest = GetManifest ( backend , backupsets [ backupsets . Count - 1 ]);
2010-05-12 08:41:54 +00:00
//Manifest version 1 does not support multiple folders
if ( latest . Version == 1 )
latest . SourceDirs = new string [] { sources [ 0 ] };
if ( latest . SourceDirs . Length != sources . Length )
{
if ( m_options . FullIfSourceFolderChanged )
{
Logging . Log . WriteMessage ( "Source folder count changed, issuing full backup" , Duplicati . Library . Logging . LogMessageType . Information );
2012-03-17 20:01:29 +00:00
if (! full )
bs . SetTypeReason ( Strings . Interface . FullBecauseSourceFoldersChanged );
2010-05-12 08:41:54 +00:00
full = true ;
}
else
throw new Exception ( string . Format ( Strings . Interface . NumberOfSourceFoldersHasChangedError , latest . SourceDirs . Length , sources . Length ));
}
else
{
if (! m_options . AllowSourceFolderChange )
{
foreach ( string s1 in latest . SourceDirs )
{
bool found = false ;
foreach ( string s2 in sources )
2010-11-21 11:08:15 +00:00
if ( s1 . Equals ( s2 , Utility . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
2010-05-12 08:41:54 +00:00
{
found = true ;
break ;
}
if (! found )
{
if ( m_options . FullIfSourceFolderChanged )
{
Logging . Log . WriteMessage ( "Source folders changed, issuing full backup" , Duplicati . Library . Logging . LogMessageType . Information );
2012-03-17 20:01:29 +00:00
if (! full )
bs . SetTypeReason ( Strings . Interface . FullBecauseSourceFoldersChanged );
2010-05-12 08:41:54 +00:00
full = true ;
break ; //Exit the folder loop
}
else
throw new Exception ( string . Format ( Strings . Interface . SourceFoldersHasChangedError , s1 ));
}
}
manifest . SourceDirs = latest . SourceDirs ;
}
else
{
manifest . SourceDirs = sources ;
}
}
2011-03-10 18:26:38 +00:00
2009-01-14 17:29:30 +00:00
}
2010-05-12 08:41:54 +00:00
2011-03-10 18:26:38 +00:00
DateTime backuptime = DateTime . Now ;
DateTime backupchaintime ;
2010-05-12 08:41:54 +00:00
if ( full )
{
patches . Clear ();
2009-03-11 20:56:01 +00:00
m_incrementalFraction = 0.0 ;
2010-05-12 08:41:54 +00:00
manifest . SourceDirs = sources ;
2011-03-10 18:26:38 +00:00
if ( m_options . CreateVerificationFile )
verification = new VerificationFile ( new ManifestEntry [ 0 ], backend . FilenameStrategy );
backupchaintime = backuptime ;
}
else
{
backupchaintime = patches [ 0 ]. Key . Time ;
manifest . PreviousManifestFilename = patches [ patches . Count - 1 ]. Key . Filename ;
manifest . PreviousManifestHash = patches [ patches . Count - 1 ]. Key . RemoteHash ;
2010-05-12 08:41:54 +00:00
}
2009-03-11 20:56:01 +00:00
2009-01-14 17:29:30 +00:00
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , - 1 , - 1 , Strings . Interface . StatusBuildingFilelist , "" );
2010-09-12 21:50:09 +00:00
2012-05-15 17:59:40 +00:00
bool completedWithoutChanges = true ;
2011-10-30 14:31:46 +00:00
2012-05-14 18:48:21 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( manifest . SourceDirs , bs , backend . Metadata , m_options . Filter , patches ))
2009-01-25 19:33:36 +00:00
{
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-03-26 19:39:50 +00:00
dir . ProgressEvent += new Duplicati . Library . Main . RSync . RSyncDir . ProgressEventDelegate ( BackupRSyncDir_ProgressEvent );
2009-03-11 20:56:01 +00:00
2009-03-22 22:55:04 +00:00
dir . DisableFiletimeCheck = m_options . DisableFiletimeCheck ;
2009-03-29 18:06:02 +00:00
dir . MaxFileSize = m_options . SkipFilesLargerThan ;
2009-01-25 19:33:36 +00:00
using ( new Logging . Timer ( "Initiating multipass" ))
2010-11-17 19:45:25 +00:00
dir . InitiateMultiPassDiff ( full , m_options );
2009-01-14 17:29:30 +00:00
2013-01-21 11:43:41 +00:00
string tempVolumeFolder = m_options . AsynchronousUpload ? m_options . AsynchronousUploadFolder : m_options . TempDir ;
2010-08-17 16:09:36 +00:00
2009-01-25 19:33:36 +00:00
bool done = false ;
2009-03-22 22:55:04 +00:00
while (! done && totalsize < m_options . MaxSize )
2009-01-14 17:29:30 +00:00
{
2009-01-25 19:33:36 +00:00
using ( new Logging . Timer ( "Multipass " + ( vol + 1 ). ToString ()))
2010-11-21 11:08:15 +00:00
using ( Utility . TempFile signaturefile = new Duplicati . Library . Utility . TempFile ( System . IO . Path . Combine ( tempVolumeFolder , Guid . NewGuid (). ToString ())))
using ( Utility . TempFile contentfile = new Duplicati . Library . Utility . TempFile ( System . IO . Path . Combine ( tempVolumeFolder , Guid . NewGuid (). ToString ())))
2009-01-14 17:29:30 +00:00
{
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusCreatingVolume , vol + 1 ), "" );
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-02-15 23:27:14 +00:00
2012-08-07 17:42:09 +00:00
using ( CompressionWrapper signaturearchive = CompressionWrapper . GetModule ( m_options . CompressionModule , signaturefile , m_options . RawOptions ))
using ( CompressionWrapper contentarchive = CompressionWrapper . GetModule ( m_options . CompressionModule , contentfile , m_options . RawOptions ))
2009-01-25 19:33:36 +00:00
{
2010-05-12 08:41:54 +00:00
//If we are all out, stop now, this may cause incomplete partial files
2010-06-19 21:08:21 +00:00
if ( m_options . MaxSize - totalsize < ( contentarchive . FlushBufferSize + backend . FileSizeOverhead ))
2010-05-12 08:41:54 +00:00
break ;
2009-02-14 17:30:52 +00:00
//Add signature files to archive
foreach ( string s in controlfiles )
if (! string . IsNullOrEmpty ( s ))
2012-08-07 17:42:09 +00:00
using ( System . IO . Stream cs = signaturearchive . CreateFile ( System . IO . Path . Combine ( RSync . RSyncDir . CONTROL_ROOT , System . IO . Path . GetFileName ( s )), DateTime . Now ))
2009-02-14 17:30:52 +00:00
using ( System . IO . FileStream fs = System . IO . File . OpenRead ( s ))
2010-11-21 11:08:15 +00:00
Utility . Utility . CopyStream ( fs , cs );
2009-02-14 17:30:52 +00:00
//Only add control files to the very first volume
controlfiles . Clear ();
2010-06-19 21:08:21 +00:00
done = dir . MakeMultiPassDiff ( signaturearchive , contentarchive , ( Math . Min ( m_options . VolumeSize , m_options . MaxSize - totalsize )) - backend . FileSizeOverhead );
2009-02-10 23:29:01 +00:00
2010-06-19 21:08:21 +00:00
//TODO: This is not the correct size, we need to account for file size overhead as well
2009-02-10 23:29:01 +00:00
totalsize += signaturearchive . Size ;
2010-05-12 08:41:54 +00:00
totalsize += contentarchive . Size ;
2009-02-10 23:29:01 +00:00
2010-05-12 08:41:54 +00:00
//TODO: This is not the best way to determine this
2009-03-22 22:55:04 +00:00
if ( totalsize >= m_options . MaxSize )
2010-05-12 08:41:54 +00:00
dir . FinalizeMultiPass ( signaturearchive , contentarchive , long . MaxValue );
2009-01-25 19:33:36 +00:00
}
2009-03-11 20:56:01 +00:00
2011-10-30 14:31:46 +00:00
completedWithoutChanges = done && ! dir . AnyChangesFound ;
2011-11-29 09:10:54 +00:00
if ( m_options . UploadUnchangedBackups || full )
2011-10-30 14:31:46 +00:00
completedWithoutChanges = false ;
if (! completedWithoutChanges )
2010-08-25 20:59:47 +00:00
{
2010-01-03 22:46:58 +00:00
2011-10-30 14:31:46 +00:00
if ( m_options . AsynchronousUpload )
{
m_lastProgressMessage = Strings . Interface . StatusWaitingForUpload ;
m_allowUploadProgress = true ;
m_allowUploadProgressAfter = DateTime . Now . AddSeconds ( 1 );
}
else
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingContentVolume , vol + 1 ), "" );
2009-01-20 17:17:46 +00:00
2011-10-30 14:31:46 +00:00
//Last check before we upload, we do not interrupt transfers
CheckLiveControl ();
2010-05-12 08:41:54 +00:00
2011-10-30 14:31:46 +00:00
//The backendwrapper will remove these
signaturefile . Protected = true ;
contentfile . Protected = true ;
2011-03-11 18:42:20 +00:00
2011-10-30 14:31:46 +00:00
ContentEntry ce = new ContentEntry ( backuptime , full , vol + 1 );
SignatureEntry se = new SignatureEntry ( backuptime , full , vol + 1 );
2009-01-14 17:29:30 +00:00
2011-10-30 14:31:46 +00:00
using ( new Logging . Timer ( "Writing delta file " + ( vol + 1 ). ToString ()))
backend . Put ( ce , contentfile );
2011-03-10 18:26:38 +00:00
2011-10-30 14:31:46 +00:00
if (! m_options . AsynchronousUpload )
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingSignatureVolume , vol + 1 ), "" );
2011-03-11 18:42:20 +00:00
2011-10-30 14:31:46 +00:00
using ( new Logging . Timer ( "Writing remote signatures" ))
backend . Put ( se , signaturefile );
2011-03-11 18:42:20 +00:00
2011-10-30 14:31:46 +00:00
manifest . AddEntries ( ce , se );
if ( verification != null )
{
verification . AddFile ( ce );
verification . AddFile ( se );
}
2011-03-10 18:26:38 +00:00
}
2009-01-15 10:46:57 +00:00
}
2009-02-10 23:29:01 +00:00
2011-10-30 14:31:46 +00:00
if (! completedWithoutChanges )
2009-02-10 23:29:01 +00:00
{
2011-10-30 14:31:46 +00:00
//The backend wrapper will remove these
Utility . TempFile mf = new Duplicati . Library . Utility . TempFile ();
2009-02-10 23:29:01 +00:00
2011-10-30 14:31:46 +00:00
using ( new Logging . Timer ( "Writing manifest " + backuptime . ToUniversalTime (). ToString ( "yyyyMMddTHHmmssK" )))
{
//Alternate primary/secondary
ManifestEntry mfe = new ManifestEntry ( backuptime , full , manifest . SignatureHashes . Count % 2 != 0 );
manifest . SelfFilename = backend . GenerateFilename ( mfe );
manifest . Save ( mf );
2009-03-11 20:56:01 +00:00
2011-10-30 14:31:46 +00:00
if (! m_options . AsynchronousUpload )
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingManifestVolume , vol + 1 ), "" );
2011-03-10 18:26:38 +00:00
2011-10-30 14:31:46 +00:00
//Write the file
mf . Protected = true ;
backend . Put ( mfe , mf );
2011-03-10 18:26:38 +00:00
2011-10-30 14:31:46 +00:00
if ( verification != null )
verification . UpdateManifest ( mfe );
}
if ( verification != null )
2011-03-10 18:26:38 +00:00
{
2011-10-30 14:31:46 +00:00
using ( new Logging . Timer ( "Writing verification " + backuptime . ToUniversalTime (). ToString ( "yyyyMMddTHHmmssK" )))
{
Utility . TempFile vt = new Duplicati . Library . Utility . TempFile ();
2011-03-10 18:26:38 +00:00
2011-10-30 14:31:46 +00:00
verification . Save ( vt );
2011-03-10 18:26:38 +00:00
2011-10-30 14:31:46 +00:00
if (! m_options . AsynchronousUpload )
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusUploadingVerificationVolume , "" );
2011-03-11 20:12:53 +00:00
2011-10-30 14:31:46 +00:00
vt . Protected = true ;
backend . Put ( new VerificationEntry ( backupchaintime ), vt );
}
2011-03-10 18:26:38 +00:00
}
2009-02-10 23:29:01 +00:00
2011-10-30 14:31:46 +00:00
if ( m_options . AsynchronousUpload )
m_allowUploadProgress = false ;
2009-03-30 20:17:54 +00:00
2011-10-30 14:31:46 +00:00
//The file volume counter
vol ++;
}
2009-01-14 17:29:30 +00:00
}
2012-05-15 17:59:40 +00:00
if (! completedWithoutChanges )
{
if ( full )
{
backend . Metadata . CurrentChainLength = 1 ;
backend . Metadata . CurrentFullDate = backupchaintime ;
backend . Metadata . FullBackupCount ++;
}
else
{
backend . Metadata . CurrentChainLength ++;
}
backend . Metadata . LastBackupDate = backupchaintime ;
backend . Metadata . LongestChainLength = Math . Max ( backend . Metadata . CurrentChainLength , backend . Metadata . LongestChainLength );
backend . Metadata . TotalVolumeCount += vol ;
backend . Metadata . TotalBackupSets ++;
}
2009-01-14 17:29:30 +00:00
}
2010-08-25 20:59:47 +00:00
2011-10-30 14:31:46 +00:00
2010-08-25 20:59:47 +00:00
//If we are running asynchronous, we now enter the end-game
if ( m_options . AsynchronousUpload )
{
m_lastProgressMessage = Strings . Interface . StatusWaitingForUpload ;
m_allowUploadProgress = true ;
m_allowUploadProgressAfter = DateTime . Now ;
//Before we clear the temp folder, we need to ensure that all volumes are uploaded.
//To allow the UI to show some progress while uploading, we perform the remaining
// uploads synchronous
List < KeyValuePair < BackupEntryBase , string >> pendingUploads = backend . ExtractPendingUploads ();
//Figure out what volume number we are at
foreach ( KeyValuePair < BackupEntryBase , string > p in pendingUploads )
if ( p . Key is ManifestEntry )
vol --;
double unitcost = m_asyncReserved / pendingUploads . Count ;
//The upload each remaining volume in order
foreach ( KeyValuePair < BackupEntryBase , string > p in pendingUploads )
{
string msg ;
if ( p . Key is ManifestEntry )
{
vol ++;
msg = string . Format ( Strings . Interface . StatusUploadingManifestVolume , vol );
}
else if ( p . Key is SignatureEntry )
msg = string . Format ( Strings . Interface . StatusUploadingSignatureVolume , (( SignatureEntry ) p . Key ). Volumenumber );
else if ( p . Key is ContentEntry )
{
msg = string . Format ( Strings . Interface . StatusUploadingContentVolume , (( ContentEntry ) p . Key ). Volumenumber );
//We allow a stop or pause request here
CheckLiveControl ();
}
2011-03-11 20:12:53 +00:00
else if ( p . Key is VerificationEntry )
msg = Strings . Interface . StatusUploadingVerificationVolume ;
2010-08-25 20:59:47 +00:00
else
throw new InvalidOperationException ();
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , ( int )( m_progress * 100 ), - 1 , msg , "" );
2010-08-25 20:59:47 +00:00
backend . Put ( p . Key , p . Value );
m_asyncReserved -= unitcost ;
m_progress += unitcost ;
}
}
2009-01-15 10:46:57 +00:00
}
2009-01-25 19:33:36 +00:00
}
2009-03-30 20:17:54 +00:00
catch ( Exception ex )
{
2012-08-15 18:33:46 +00:00
//May be overwritten if this is a partial success
m_result = ex ;
//If this is a controlled user-requested stop, wait for the current upload to complete
2010-08-25 20:59:47 +00:00
if ( backend != null && ex is LiveControl . ExecutionStoppedException )
{
try
{
if ( m_options . AsynchronousUpload )
{
m_lastProgressMessage = Strings . Interface . StatusWaitingForUpload ;
m_allowUploadProgress = true ;
m_allowUploadProgressAfter = DateTime . Now ;
//Wait for the current upload to complete and then delete all remaining temporary files
foreach ( KeyValuePair < BackupEntryBase , string > p in backend . ExtractPendingUploads ())
try
{
if ( System . IO . File . Exists ( p . Value ))
System . IO . File . Delete ( p . Value );
}
catch { } //Better to delete as many as possible rather than choke on a single file
}
}
catch { } //We already have an exception, just go with that
}
if ( backend == null || backend . ManifestUploads == 0 )
2012-06-29 19:38:04 +00:00
{
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . ErrorRunningBackup , ex . Message ), Logging . LogMessageType . Error );
2009-03-30 20:17:54 +00:00
throw ; //This also activates "finally", unlike in other languages...
2012-06-29 19:38:04 +00:00
}
2009-03-30 20:17:54 +00:00
2012-06-29 19:38:04 +00:00
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . PartialUploadMessage , backend . ManifestUploads , ex . Message ), Logging . LogMessageType . Warning );
2010-09-11 12:43:55 +00:00
bs . LogError ( string . Format ( Strings . Interface . PartialUploadMessage , backend . ManifestUploads , ex . Message ), ex );
2012-08-07 19:36:49 +00:00
bs . PartialBackup = true ;
2009-03-30 20:17:54 +00:00
}
2009-01-25 19:33:36 +00:00
finally
{
2009-03-11 20:56:01 +00:00
m_progress = 100.0 ;
2012-05-14 18:48:21 +00:00
if ( MetadataReport != null && backend != null )
MetadataReport ( backend . Metadata . AsReport ());
2009-01-25 19:33:36 +00:00
if ( backend != null )
2010-08-25 20:59:47 +00:00
try { backend . Dispose (); }
catch { }
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2010-09-26 20:43:16 +00:00
OperationCompleted ( this , DuplicatiOperation . Backup , bs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-03-26 19:39:50 +00:00
2011-06-17 13:31:28 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , bs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-01-25 19:33:36 +00:00
}
2009-01-14 17:29:30 +00:00
}
2009-01-17 15:27:51 +00:00
bs . EndTime = DateTime . Now ;
2012-08-15 18:33:46 +00:00
m_result = bs ;
2009-01-17 15:27:51 +00:00
return bs . ToString ();
2009-01-14 17:29:30 +00:00
}
2010-08-25 20:59:47 +00:00
/// <summary>
/// Event handler that is activated when an asynchronous upload is about to being,
/// used to pause uploads if the user has requested a pause
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void backend_AsyncItemProcessedEvent ( object sender , EventArgs e )
{
m_liveControl . PauseIfRequested ();
}
2010-01-16 16:18:59 +00:00
/// <summary>
/// Event handler for reporting upload/download progress
/// </summary>
/// <param name="progress">The upload/download progress in percent</param>
/// <param name="filename">The name of the file being transfered</param>
private void BackupTransfer_ProgressEvent ( int progress , string filename )
{
2010-08-25 20:59:47 +00:00
if ( m_allowUploadProgress && DateTime . Now > m_allowUploadProgressAfter )
2010-09-26 20:43:16 +00:00
OperationProgress ( this , GetOperationType (), m_options . MainAction , ( int )( m_progress * 100 ), progress , m_lastProgressMessage , filename );
2010-01-16 16:18:59 +00:00
}
/// <summary>
2010-09-26 20:43:16 +00:00
/// Event handler for reporting backup progress
2010-01-16 16:18:59 +00:00
/// </summary>
/// <param name="progress">The total progress in percent</param>
/// <param name="filename">The file currently being examined</param>
2009-03-11 20:56:01 +00:00
private void BackupRSyncDir_ProgressEvent ( int progress , string filename )
{
m_progress = (( 1.0 - m_incrementalFraction ) * ( progress / ( double ) 100.0 )) + m_incrementalFraction ;
2010-08-25 20:59:47 +00:00
m_progress *= ( 1.0 - m_asyncReserved );
2010-09-26 20:43:16 +00:00
OperationProgress ( this , GetOperationType (), m_options . MainAction , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusProcessing , filename ), "" );
CheckLiveControl ();
}
/// <summary>
/// Event handler for reporting restore progress
/// </summary>
/// <param name="progress">The total progress in percent</param>
/// <param name="filename">The file currently being examined</param>
private void RestoreRSyncDir_ProgressEvent ( int progress , string filename )
{
double pgPrPatch = (( 1.0 - INCREMENAL_COST ) / m_restorePatches );
int fullProgress = ( int )(( m_progress * 100 ) + ( pgPrPatch * ( progress / 100.0 )));
OperationProgress ( this , GetOperationType (), m_options . MainAction , fullProgress , - 1 , string . Format ( Strings . Interface . StatusProcessing , filename ), "" );
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-03-11 20:56:01 +00:00
}
2011-03-10 18:26:38 +00:00
/// <summary>
/// Validates the manifest chain, starting by evaluating the current manifest and going backwards in the chain
/// </summary>
/// <param name="backend">The backend wrapper</param>
/// <param name="entry">The entry to verify</param>
private void VerifyManifestChain ( BackendWrapper backend , ManifestEntry entry )
{
if ( m_options . DontReadManifests )
throw new InvalidOperationException ( Strings . Interface . CannotVerifyChain );
while ( entry . Previous != null )
{
2011-10-22 14:59:47 +00:00
Manifestfile parsed = GetManifest ( backend , entry );
2011-03-10 18:26:38 +00:00
//If this manifest is not version 3, the chain verification stops here
2011-10-22 14:59:47 +00:00
if ( parsed . Version < 3 )
2011-03-10 18:26:38 +00:00
return ;
ManifestEntry previous = entry . Previous ;
2011-10-22 14:59:47 +00:00
if ( entry . Previous . Alternate != null && entry . Previous . Alternate . Filename == parsed . PreviousManifestFilename )
2011-03-10 18:26:38 +00:00
previous = entry . Previous . Alternate ;
2011-10-22 14:59:47 +00:00
if ( parsed . PreviousManifestFilename != previous . Filename )
throw new System . IO . InvalidDataException ( string . Format ( Strings . Interface . PreviousManifestFilenameMismatchError , entry . Filename , parsed . PreviousManifestFilename , previous . Filename ));
2011-03-10 18:26:38 +00:00
if (! m_options . SkipFileHashChecks )
{
2011-10-22 14:59:47 +00:00
//Load the Remotehash property
GetManifest ( backend , previous );
if ( parsed . PreviousManifestHash != previous . RemoteHash )
throw new System . IO . InvalidDataException ( string . Format ( Strings . Interface . PreviousManifestHashMismatchError , entry . Filename , parsed . PreviousManifestHash , previous . RemoteHash ));
2011-03-10 18:26:38 +00:00
}
entry = previous ;
}
}
/// <summary>
/// Verifies the backup chain for producing a new backup on top.
/// This will check that all files are accounted for in the file list.
/// </summary>
2011-03-11 18:42:20 +00:00
/// <param name="entry">The newest entry to check</param>
2011-03-10 18:26:38 +00:00
private void VerifyBackupChainWithFiles ( BackendWrapper backend , ManifestEntry entry )
{
VerifyManifestChain ( backend , entry );
2011-03-16 20:11:20 +00:00
string errorMessage = Environment . NewLine + Strings . Interface . DeleteManifestsSuggestion + Environment . NewLine + Environment . NewLine ;
2011-03-10 18:26:38 +00:00
while ( entry != null )
{
2011-10-22 14:59:47 +00:00
Manifestfile parsed = GetManifest ( backend , entry );
2011-03-16 20:11:20 +00:00
errorMessage += entry . Filename + Environment . NewLine ;
2011-10-22 14:59:47 +00:00
if ( entry . Volumes . Count != parsed . SignatureHashes . Count || entry . Volumes . Count != parsed . ContentHashes . Count )
2011-03-16 20:11:20 +00:00
{
2011-10-22 14:59:47 +00:00
//If we have an extra set, the connection could have died right before the manifest was uploaded
if ( parsed . SignatureHashes . Count == parsed . ContentHashes . Count && entry . Volumes . Count - 1 == parsed . ContentHashes . Count )
{
backend . AddOrphan ( entry . Volumes [ entry . Volumes . Count - 1 ]. Value );
backend . AddOrphan ( entry . Volumes [ entry . Volumes . Count - 1 ]. Key );
entry . Volumes . RemoveAt ( entry . Volumes . Count - 1 );
}
else
{
throw new Exception (
string . Format ( Strings . Interface . ManifestAndFileCountMismatchError , entry . Filename , parsed . SignatureHashes . Count , entry . Volumes . Count )
+ errorMessage
);
}
2011-03-16 20:11:20 +00:00
}
2011-03-11 18:42:20 +00:00
for ( int i = 0 ; i < entry . Volumes . Count ; i ++)
{
2011-10-22 14:59:47 +00:00
if ( entry . Volumes [ i ]. Key . Filesize > 0 && parsed . SignatureHashes [ i ]. Size > 0 && entry . Volumes [ i ]. Key . Filesize != parsed . SignatureHashes [ i ]. Size )
2011-03-16 20:11:20 +00:00
throw new Exception (
2011-10-22 14:59:47 +00:00
string . Format ( Strings . Interface . FileSizeMismatchError , entry . Volumes [ i ]. Key . Filename , entry . Volumes [ i ]. Key . Filesize , parsed . SignatureHashes [ i ]. Size )
2011-03-16 20:11:20 +00:00
+ errorMessage
);
2011-03-11 18:42:20 +00:00
2011-10-22 14:59:47 +00:00
if ( entry . Volumes [ i ]. Value . Filesize >= 0 && parsed . ContentHashes [ i ]. Size >= 0 && entry . Volumes [ i ]. Value . Filesize != parsed . ContentHashes [ i ]. Size )
2011-03-16 20:11:20 +00:00
throw new Exception (
2011-10-22 14:59:47 +00:00
string . Format ( Strings . Interface . FileSizeMismatchError , entry . Volumes [ i ]. Value . Filename , entry . Volumes [ i ]. Value . Filesize , parsed . ContentHashes [ i ]. Size )
2011-03-16 20:11:20 +00:00
+ errorMessage
);
2011-03-11 18:42:20 +00:00
2011-10-22 14:59:47 +00:00
if (! string . IsNullOrEmpty ( parsed . SignatureHashes [ i ]. Name ) && ! parsed . SignatureHashes [ i ]. Name . Equals ( entry . Volumes [ i ]. Key . Fileentry . Name , StringComparison . InvariantCultureIgnoreCase ))
2011-03-16 20:11:20 +00:00
throw new Exception (
2011-10-22 14:59:47 +00:00
string . Format ( Strings . Interface . FilenameMismatchError , parsed . SignatureHashes [ i ]. Name , entry . Volumes [ i ]. Key . Fileentry . Name )
2011-03-16 20:11:20 +00:00
+ errorMessage
);
2011-03-11 18:42:20 +00:00
2011-10-22 14:59:47 +00:00
if (! string . IsNullOrEmpty ( parsed . ContentHashes [ i ]. Name ) && ! parsed . ContentHashes [ i ]. Name . Equals ( entry . Volumes [ i ]. Value . Fileentry . Name , StringComparison . InvariantCultureIgnoreCase ))
2011-03-16 20:11:20 +00:00
throw new Exception (
2011-10-22 14:59:47 +00:00
string . Format ( Strings . Interface . FilenameMismatchError , parsed . ContentHashes [ i ]. Name , entry . Volumes [ i ]. Value . Fileentry . Name )
2011-03-16 20:11:20 +00:00
+ errorMessage
);
2011-03-11 18:42:20 +00:00
}
2011-03-10 18:26:38 +00:00
entry = entry . Previous ;
}
}
2010-05-12 08:41:54 +00:00
/// <summary>
2010-09-30 12:39:52 +00:00
/// Will attempt to read the manifest file, optionally reverting to the secondary manifest if reading one fails.
2010-05-12 08:41:54 +00:00
/// </summary>
/// <param name="backend">The backendwrapper to read from</param>
/// <param name="entry">The manifest to read</param>
/// <returns>The parsed manifest</returns>
private Manifestfile GetManifest ( BackendWrapper backend , ManifestEntry entry )
{
if ( m_options . DontReadManifests )
{
Manifestfile mf = new Manifestfile ();
mf . SignatureHashes = null ;
mf . ContentHashes = null ;
return mf ;
}
2011-03-10 18:26:38 +00:00
if ( entry . ParsedManifest != null )
return entry . ParsedManifest ;
else if ( entry . Alternate != null && entry . Alternate . ParsedManifest != null )
return entry . Alternate . ParsedManifest ;
2011-03-12 10:03:54 +00:00
if ( OperationProgress != null && backend . Statistics != null )
OperationProgress ( this , GetOperationType (), backend . Statistics . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusReadingManifest , entry . Time . ToShortDateString () + " " + entry . Time . ToShortTimeString ()), "" );
2011-10-22 14:59:47 +00:00
bool tryAlternateManifest = false ;
//This method has some very special logic to ensure correct handling of errors
//The assumption is that it is possible to determine if the error occurred due to a
// transfer problem or a corrupt file. If the former happens, the operation should
// be retried, and thus an exception is thrown. If the latter, the file should
// be ignored and the backup file should be used.
//
//We detect a parsing error, either directly or indirectly through CryptographicException,
// and assume that a parsing error is an indication of a broken file.
//All other errors are assumed to be transfer problems, and throws exceptions.
//
//This holds as long as the backend always throws an exception if a partial file
// was downloaded. The FTP backend may not honor this, and some webservers
// may ommit the "Content-Length" header, which will cause problems.
//There is a guard agains partial downloads in BackendWrapper.GetInternal()
2010-05-12 08:41:54 +00:00
using ( new Logging . Timer ( "Get " + entry . Filename ))
2010-11-21 11:08:15 +00:00
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
2010-05-12 08:41:54 +00:00
{
try
{
2011-03-10 18:26:38 +00:00
backend . Get ( entry , null , tf , null );
2010-05-12 08:41:54 +00:00
//We now have the file decrypted, if the next step fails,
// its a broken xml or invalid content
2011-10-22 14:59:47 +00:00
tryAlternateManifest = true ;
2011-03-10 18:26:38 +00:00
Manifestfile mf = new Manifestfile ( tf , m_options . SkipFileHashChecks );
2011-10-22 14:59:47 +00:00
if ( string . IsNullOrEmpty ( mf . SelfFilename ))
mf . SelfFilename = entry . Filename ;
if ( mf . ContentHashes != null && entry . Alternate != null )
{
//Special case, the manifest has not recorded all volumes,
// we must see if the alternate manifest has more volumes
if ( entry . Volumes . Count > mf . ContentHashes . Count )
{
//Do not try the alternate, we just did
tryAlternateManifest = false ;
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . ReadingSecondaryManifestLogMessage , entry . Alternate . Filename ), Duplicati . Library . Logging . LogMessageType . Information );
Manifestfile amf = null ;
//Read the alternate file and try to differentiate between a defect file or a partial one
bool defectFile = false ;
try
{
System . IO . File . Delete ( tf );
backend . Get ( entry . Alternate , null , tf , null );
}
catch ( System . Security . Cryptography . CryptographicException cex )
{
//We assume that CryptoException means partial file
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . SecondaryManifestReadErrorLogMessage , entry . Alternate . Filename , cex ), Duplicati . Library . Logging . LogMessageType . Warning );
defectFile = true ;
}
if (! defectFile )
{
try
{
amf = new Manifestfile ( tf , m_options . SkipFileHashChecks );
}
catch ( Exception ex )
{
//Parsing error means partial file
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . SecondaryManifestReadErrorLogMessage , entry . Alternate . Filename , ex ), Duplicati . Library . Logging . LogMessageType . Warning );
defectFile = true ;
}
}
//If the alternate manifest is correct, assign it so we have a copy
if (! defectFile && amf != null )
{
if ( string . IsNullOrEmpty ( amf . SelfFilename ))
amf . SelfFilename = entry . Alternate . Filename ;
//If the alternate manifest has more files than the primary, we use that one
if ( amf . ContentHashes != null && amf . ContentHashes . Count > mf . ContentHashes . Count )
{
entry . Alternate . ParsedManifest = amf ;
if ( m_options . SkipFileHashChecks )
{
mf . SignatureHashes = null ;
mf . ContentHashes = null ;
}
return amf ;
}
}
}
}
2010-05-12 08:41:54 +00:00
if ( m_options . SkipFileHashChecks )
{
mf . SignatureHashes = null ;
mf . ContentHashes = null ;
}
2011-03-10 18:26:38 +00:00
entry . ParsedManifest = mf ;
2010-05-12 08:41:54 +00:00
return mf ;
}
catch ( Exception ex )
{
//Only try secondary if the parsing/decrypting fails, not if the transfer fails
2011-10-22 14:59:47 +00:00
if ( entry . Alternate != null && ( ex is System . Security . Cryptography . CryptographicException || tryAlternateManifest ))
2010-05-12 08:41:54 +00:00
{
//TODO: If it is a version error, there is no need to read the alternate version
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . PrimaryManifestReadErrorLogMessage , entry . Filename , ex . Message ), Duplicati . Library . Logging . LogMessageType . Warning );
try
{
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . ReadingSecondaryManifestLogMessage , entry . Alternate . Filename ), Duplicati . Library . Logging . LogMessageType . Information );
return GetManifest ( backend , entry . Alternate );
}
catch ( Exception ex2 )
{
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . SecondaryManifestReadErrorLogMessage , entry . Alternate . Filename , ex2 . Message ), Duplicati . Library . Logging . LogMessageType . Warning );
}
}
//Report the original error
throw ;
}
}
}
public string Restore ( string [] target )
2009-01-14 17:29:30 +00:00
{
2010-09-26 20:43:16 +00:00
RestoreStatistics rs = new RestoreStatistics ( DuplicatiOperationMode . Restore );
2012-08-15 18:33:46 +00:00
SetupCommonOptions ( rs , ref target );
2009-01-14 17:29:30 +00:00
2010-01-16 16:18:59 +00:00
m_progress = 0 ;
2009-03-26 19:39:50 +00:00
BackendWrapper backend = null ;
2010-09-26 20:43:16 +00:00
m_restorePatches = 0 ;
2009-01-25 19:33:36 +00:00
2010-10-12 22:22:29 +00:00
using ( new Logging . Timer ( "Restore from " + m_backend + " to " + string . Join ( System . IO . Path . PathSeparator . ToString (), target )))
2009-01-14 17:29:30 +00:00
{
2009-01-25 19:33:36 +00:00
try
2011-06-17 13:31:28 +00:00
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2010-09-26 20:43:16 +00:00
OperationStarted ( this , DuplicatiOperation . Restore , rs . OperationMode , - 1 , - 1 , Strings . Interface . StatusStarted , "" );
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , - 1 , - 1 , Strings . Interface . StatusStarted , "" );
2009-02-15 23:27:14 +00:00
2010-11-21 11:08:15 +00:00
Utility . FilenameFilter filter = m_options . Filter ;
2009-02-12 21:52:58 +00:00
//Filter is prefered, if both file and filter is specified
2009-03-22 22:55:04 +00:00
if (! m_options . HasFilter && ! string . IsNullOrEmpty ( m_options . FileToRestore ))
2009-02-12 21:52:58 +00:00
{
2010-11-21 11:08:15 +00:00
List < Utility . IFilenameFilter > list = new List < Duplicati . Library . Utility . IFilenameFilter >();
list . Add ( new Utility . FilelistFilter ( true , m_options . FileToRestore . Split ( System . IO . Path . PathSeparator )));
list . Add ( new Utility . RegularExpressionFilter ( false , ".*" ));
2009-02-12 21:52:58 +00:00
2010-11-21 11:08:15 +00:00
filter = new Duplicati . Library . Utility . FilenameFilter ( list );
2009-02-12 21:52:58 +00:00
}
2009-03-26 19:39:50 +00:00
backend = new BackendWrapper ( rs , m_backend , m_options );
2010-01-16 16:18:59 +00:00
backend . ProgressEvent += new Duplicati . Library . Main . RSync . RSyncDir . ProgressEventDelegate ( BackupTransfer_ProgressEvent );
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusReadingIncrementals , "" );
2009-01-25 19:33:36 +00:00
2010-05-12 08:41:54 +00:00
ManifestEntry bestFit = backend . GetBackupSet ( m_options . RestoreTime );
2009-01-14 17:29:30 +00:00
2011-03-10 18:26:38 +00:00
//We will need all the manifests downloaded anyway
if (! m_options . DontReadManifests )
{
if ( bestFit . Incrementals . Count > 0 )
VerifyManifestChain ( backend , bestFit . Incrementals [ bestFit . Incrementals . Count - 1 ]);
else
VerifyManifestChain ( backend , bestFit );
2011-03-12 10:03:54 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusReadingIncrementals , "" );
2011-03-10 18:26:38 +00:00
}
2010-01-16 16:18:59 +00:00
m_progress = INCREMENAL_COST ;
2010-05-12 08:41:54 +00:00
List < ManifestEntry > entries = new List < ManifestEntry >();
2009-02-10 23:29:01 +00:00
entries . Add ( bestFit );
entries . AddRange ( bestFit . Incrementals );
int patchno = 0 ;
2010-09-26 20:43:16 +00:00
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2010-09-26 20:43:16 +00:00
m_restorePatches += be . Volumes . Count ;
2010-01-16 16:18:59 +00:00
2010-10-12 22:22:29 +00:00
Manifestfile rootManifest = GetManifest ( backend , bestFit );
2010-11-05 06:48:00 +00:00
int sourceDirCount = ( rootManifest . SourceDirs == null || rootManifest . SourceDirs . Length == 0 ) ? 1 : rootManifest . SourceDirs . Length ;
2010-10-12 22:22:29 +00:00
//After reading the first manifest, we know the source folder count
if (( rootManifest . SourceDirs == null || rootManifest . SourceDirs . Length == 0 ) && target . Length > 1 )
{
//V1 support
rs . LogWarning ( string . Format ( Strings . Interface . TooManyTargetFoldersWarning , 1 , target . Length ), null );
Array . Resize ( ref target , 1 );
}
2010-11-05 06:48:00 +00:00
else if ( target . Length > sourceDirCount )
2010-10-12 22:22:29 +00:00
{
//If we get too many, we can just cut them off
2010-11-05 06:48:00 +00:00
rs . LogWarning ( string . Format ( Strings . Interface . TooManyTargetFoldersWarning , sourceDirCount , target . Length ), null );
2010-10-12 22:22:29 +00:00
Array . Resize ( ref target , rootManifest . SourceDirs . Length );
}
2010-11-05 06:48:00 +00:00
else if ( target . Length != 1 && target . Length < sourceDirCount )
2010-10-12 22:22:29 +00:00
{
//If we get too few, we have to bail
2010-11-05 06:48:00 +00:00
throw new Exception ( string . Format ( Strings . Interface . TooFewTargetFoldersError , sourceDirCount , target . Length ));
2010-10-12 22:22:29 +00:00
}
2010-11-05 06:48:00 +00:00
else if ( target . Length == 1 && sourceDirCount > 1 )
2010-10-12 22:22:29 +00:00
{
//If there is just one target folder, we automatically compose target subfolders
string [] newtargets = new string [ rootManifest . SourceDirs . Length ];
List < string > suggestions = new List < string >();
for ( int i = 0 ; i < rootManifest . SourceDirs . Length ; i ++)
{
string s = rootManifest . SourceDirs [ i ];
//HACK: We use a leading / in the path name to detect source OS
// all paths are absolute, so this detects all unix like systems
string dirSepChar = s . StartsWith ( "/" ) ? "/" : "\\" ;
if ( s . EndsWith ( dirSepChar ))
s = s . Substring ( 0 , s . Length - 1 );
int lix = s . LastIndexOf ( dirSepChar );
if ( lix < 0 || lix + 1 >= s . Length )
s = i . ToString ();
else
s = s . Substring ( lix + 1 );
foreach ( char c in System . IO . Path . GetInvalidFileNameChars ())
s = s . Replace ( c , '_' );
suggestions . Add ( s );
}
2010-11-21 11:08:15 +00:00
Dictionary < string , int > duplicates = new Dictionary < string , int >( Library . Utility . Utility . ClientFilenameStringComparer );
2010-10-12 22:22:29 +00:00
for ( int i = 0 ; i < suggestions . Count ; i ++)
if ( duplicates . ContainsKey ( suggestions [ i ]))
duplicates [ suggestions [ i ]]++;
else
duplicates [ suggestions [ i ]] = 1 ;
for ( int i = 0 ; i < newtargets . Length ; i ++)
{
string suffix = duplicates [ suggestions [ i ]] > 1 ? i . ToString () : suggestions [ i ];
newtargets [ i ] = System . IO . Path . Combine ( target [ 0 ], suffix );
}
target = newtargets ;
}
//Make sure all targets exist
foreach ( string s in target )
if (! System . IO . Directory . Exists ( s ))
System . IO . Directory . CreateDirectory ( s );
2012-05-14 18:48:21 +00:00
using ( RSync . RSyncDir sync = new Duplicati . Library . Main . RSync . RSyncDir ( target , rs , backend . Metadata , filter ))
2009-02-10 23:29:01 +00:00
{
2010-09-26 20:43:16 +00:00
sync . ProgressEvent += new Duplicati . Library . Main . RSync . RSyncDir . ProgressEventDelegate ( RestoreRSyncDir_ProgressEvent );
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2009-03-09 21:09:27 +00:00
{
2010-09-26 20:43:16 +00:00
m_progress = (( 1.0 - INCREMENAL_COST ) * ( patchno / ( double ) m_restorePatches )) + INCREMENAL_COST ;
2010-01-16 16:18:59 +00:00
2010-05-12 08:41:54 +00:00
CheckLiveControl ();
2009-03-09 21:09:27 +00:00
2010-10-12 22:22:29 +00:00
Manifestfile manifest = be == bestFit ? rootManifest : GetManifest ( backend , be );
2009-03-09 21:09:27 +00:00
2010-05-12 08:41:54 +00:00
CheckLiveControl ();
2009-03-09 21:09:27 +00:00
2010-05-12 08:41:54 +00:00
foreach ( KeyValuePair < SignatureEntry , ContentEntry > vol in be . Volumes )
2009-01-25 19:33:36 +00:00
{
2010-05-12 08:41:54 +00:00
ContentEntry contentVol = vol . Value ;
SignatureEntry signatureVol = vol . Key ;
2010-09-26 20:43:16 +00:00
m_progress = (( 1.0 - INCREMENAL_COST ) * ( patchno / ( double ) m_restorePatches )) + INCREMENAL_COST ;
2010-01-16 16:18:59 +00:00
2009-03-11 20:56:01 +00:00
//Skip nonlisted
2010-05-12 08:41:54 +00:00
if ( manifest . ContentHashes != null && contentVol . Volumenumber > manifest . ContentHashes . Count )
2010-01-16 16:18:59 +00:00
{
2010-05-12 08:41:54 +00:00
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . SkippedContentVolumeLogMessage , contentVol . Volumenumber ), Duplicati . Library . Logging . LogMessageType . Warning );
2010-09-11 12:43:55 +00:00
rs . LogWarning ( string . Format ( Strings . Interface . SkippedContentVolumeLogMessage , contentVol . Volumenumber ), null );
2010-01-16 16:18:59 +00:00
patchno ++;
2010-09-04 12:23:03 +00:00
continue ;
2010-01-16 16:18:59 +00:00
}
2009-03-11 20:56:01 +00:00
2010-11-21 11:08:15 +00:00
using ( Utility . TempFile patchzip = new Duplicati . Library . Utility . TempFile ())
2009-01-25 19:33:36 +00:00
{
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusPatching , patchno + 1 ), "" );
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-02-15 23:27:14 +00:00
2009-04-07 22:02:51 +00:00
if ( m_options . HasFilter || ! string . IsNullOrEmpty ( m_options . FileToRestore ))
{
bool hasFiles = false ;
2010-11-21 11:08:15 +00:00
using ( Utility . TempFile sigFile = new Duplicati . Library . Utility . TempFile ())
2009-04-07 22:02:51 +00:00
{
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusDownloadingSignatureVolume , patchno + 1 ), "" );
2010-01-16 16:18:59 +00:00
2010-09-04 12:23:03 +00:00
try
{
using ( new Logging . Timer ( "Get " + signatureVol . Filename ))
2011-03-10 18:26:38 +00:00
backend . Get ( signatureVol , manifest , sigFile , manifest . SignatureHashes == null ? null : manifest . SignatureHashes [ signatureVol . Volumenumber - 1 ]);
2010-09-04 12:23:03 +00:00
}
catch ( BackendWrapper . HashMismathcException hme )
2009-04-07 22:02:51 +00:00
{
2010-09-04 12:23:03 +00:00
hasFiles = true ;
2010-09-11 12:43:55 +00:00
rs . LogError ( string . Format ( Strings . Interface . FileHashFailure , hme . Message ), hme );
2009-04-07 22:02:51 +00:00
}
2010-09-04 12:23:03 +00:00
if (! hasFiles )
2012-08-07 17:42:09 +00:00
using ( CompressionWrapper patch = CompressionWrapper . GetModule ( signatureVol . Compression , sigFile , m_options . RawOptions ))
2010-09-04 12:23:03 +00:00
{
foreach ( KeyValuePair < RSync . RSyncDir . PatchFileType , string > k in sync . ListPatchFiles ( patch ))
if ( filter . ShouldInclude ( "" , System . IO . Path . DirectorySeparatorChar . ToString () + k . Value ))
{
//TODO: Perhaps a bit much to download the content archive
// if the file is only marked for deletion?
hasFiles = true ;
break ;
}
}
2009-04-07 22:02:51 +00:00
}
if (! hasFiles )
2010-12-27 16:32:41 +00:00
{
//Avoid downloading the content file
patchno ++;
continue ;
}
2010-01-16 16:18:59 +00:00
}
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusDownloadingContentVolume , patchno + 1 ), "" );
2009-04-07 22:02:51 +00:00
2010-05-12 08:41:54 +00:00
using ( new Logging . Timer ( "Get " + contentVol . Filename ))
2011-03-10 18:26:38 +00:00
backend . Get ( contentVol , manifest , patchzip , manifest . ContentHashes == null ? null : manifest . ContentHashes [ contentVol . Volumenumber - 1 ]);
2009-03-09 21:09:27 +00:00
2010-09-26 20:43:16 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusPatching , patchno + 1 ), "" );
2010-01-16 16:18:59 +00:00
2010-06-19 21:08:21 +00:00
using ( new Logging . Timer (( patchno == 0 ? "Full restore to: " : "Incremental restore " + patchno . ToString () + " to: " ) + string . Join ( System . IO . Path . PathSeparator . ToString (), target )))
2012-08-07 17:42:09 +00:00
using ( CompressionWrapper patch = CompressionWrapper . GetModule ( contentVol . Compression , patchzip , m_options . RawOptions ))
2009-03-26 19:39:50 +00:00
sync . Patch ( target , patch );
2009-01-25 19:33:36 +00:00
}
2009-02-10 23:29:01 +00:00
patchno ++;
2009-01-15 10:46:57 +00:00
}
2010-05-12 08:41:54 +00:00
//Make sure there are no partial files, as partial files are not allowed to span backup sets
sync . FinalizeRestore ();
2009-03-09 21:09:27 +00:00
}
2009-01-25 19:33:36 +00:00
}
2009-01-14 17:29:30 +00:00
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2009-01-25 19:33:36 +00:00
finally
{
2012-05-14 18:48:21 +00:00
if ( MetadataReport != null && backend != null )
MetadataReport ( backend . Metadata . AsReport ());
2009-01-25 19:33:36 +00:00
if ( backend != null )
backend . Dispose ();
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2010-09-26 20:43:16 +00:00
OperationCompleted ( this , DuplicatiOperation . Restore , rs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2010-01-16 16:18:59 +00:00
2011-06-17 13:31:28 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-01-25 19:33:36 +00:00
}
}
2009-01-17 15:27:51 +00:00
rs . EndTime = DateTime . Now ;
2012-08-15 18:33:46 +00:00
m_result = rs ;
2009-01-17 15:27:51 +00:00
return rs . ToString ();
}
2009-02-14 17:30:52 +00:00
/// <summary>
/// Restores control files added to a backup.
/// </summary>
/// <param name="target">The folder into which to restore the files</param>
/// <returns>A restore report</returns>
2009-02-15 23:27:14 +00:00
public string RestoreControlFiles ( string target )
2009-02-14 17:30:52 +00:00
{
2010-09-26 20:43:16 +00:00
RestoreStatistics rs = new RestoreStatistics ( DuplicatiOperationMode . RestoreControlfiles );
SetupCommonOptions ( rs );
2009-02-14 17:30:52 +00:00
2009-02-15 23:27:14 +00:00
using ( new Logging . Timer ( "Restore control files from " + m_backend + " to " + target ))
2009-02-14 17:30:52 +00:00
{
try
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2010-09-26 20:43:16 +00:00
OperationStarted ( this , DuplicatiOperation . Restore , rs . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-02-14 17:30:52 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( rs , m_backend , m_options ))
2009-02-14 17:30:52 +00:00
{
2012-08-15 18:33:46 +00:00
List < ManifestEntry > attempts = backend . GetBackupSets ();
List < ManifestEntry > flatlist = new List < ManifestEntry >();
foreach ( ManifestEntry be in attempts )
{
flatlist . Add ( be );
flatlist . AddRange ( be . Incrementals );
}
2009-02-14 17:30:52 +00:00
2012-08-15 18:33:46 +00:00
flatlist . Reverse ();
2009-02-15 23:27:14 +00:00
2012-08-15 18:33:46 +00:00
string prefix = Utility . Utility . AppendDirSeparator ( RSync . RSyncDir . CONTROL_ROOT );
2009-02-15 23:27:14 +00:00
2012-08-15 18:33:46 +00:00
foreach ( ManifestEntry be in flatlist )
{
if ( be . Volumes . Count > 0 )
using ( Utility . TempFile z = new Duplicati . Library . Utility . TempFile ())
{
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , 0 , - 1 , string . Format ( Strings . Interface . StatusReadingIncrementalFile , be . Volumes [ 0 ]. Key . Filename ), "" );
2009-02-15 23:27:14 +00:00
2012-08-15 18:33:46 +00:00
Manifestfile mf = GetManifest ( backend , be );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , rs . OperationMode , 0 , - 1 , string . Format ( Strings . Interface . StatusReadingIncrementalFile , be . Volumes [ 0 ]. Key . Filename ), "" );
2011-03-12 10:03:54 +00:00
2012-08-15 18:33:46 +00:00
using ( new Logging . Timer ( "Get " + be . Volumes [ 0 ]. Key . Filename ))
backend . Get ( be . Volumes [ 0 ]. Key , mf , z , mf . SignatureHashes == null ? null : mf . SignatureHashes [ 0 ]);
2012-08-07 17:42:09 +00:00
2012-08-15 18:33:46 +00:00
using ( CompressionWrapper fz = CompressionWrapper . GetModule ( be . Volumes [ 0 ]. Key . Compression , z , m_options . RawOptions ))
2009-02-14 17:30:52 +00:00
{
2012-08-15 18:33:46 +00:00
bool any = false ;
foreach ( string f in fz . ListFiles ( prefix ))
{
any = true ;
using ( System . IO . Stream s1 = fz . OpenRead ( f ))
using ( System . IO . Stream s2 = System . IO . File . Create ( System . IO . Path . Combine ( target , f . Substring ( prefix . Length ))))
Utility . Utility . CopyStream ( s1 , s2 );
}
2009-02-14 17:30:52 +00:00
2012-08-15 18:33:46 +00:00
if ( any )
break ;
2009-02-14 17:30:52 +00:00
2012-08-15 18:33:46 +00:00
rs . LogError ( string . Format ( Strings . Interface . FailedToFindControlFilesMessage , be . Volumes [ 0 ]. Key . Filename ), null );
}
2009-02-14 17:30:52 +00:00
}
2012-08-15 18:33:46 +00:00
}
2009-02-14 17:30:52 +00:00
}
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2009-02-14 17:30:52 +00:00
finally
{
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2010-09-26 20:43:16 +00:00
OperationCompleted ( this , DuplicatiOperation . Restore , rs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-02-14 17:30:52 +00:00
}
}
rs . EndTime = DateTime . Now ;
2012-08-15 18:33:46 +00:00
m_result = rs ;
2009-02-14 17:30:52 +00:00
return rs . ToString ();
}
2012-08-15 18:33:46 +00:00
public string DeleteAllButN ()
2011-10-19 05:50:10 +00:00
{
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . DeleteAllButN );
SetupCommonOptions ( stats );
int x = Math . Max ( 0 , m_options . DeleteAllButNFull );
StringBuilder sb = new StringBuilder ();
2012-08-15 18:33:46 +00:00
BackendWrapper backend = null ;
2011-10-19 05:50:10 +00:00
2012-08-15 18:33:46 +00:00
try
{
using ( backend = new BackendWrapper ( stats , m_backend , m_options ))
2011-10-19 05:50:10 +00:00
{
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . Remove , stats . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
List < ManifestEntry > flatlist = new List < ManifestEntry >();
2012-08-15 18:33:46 +00:00
List < ManifestEntry > entries = backend . GetBackupSets ();
2011-10-19 05:50:10 +00:00
//Get all backups as a flat list
2012-08-15 18:33:46 +00:00
foreach ( ManifestEntry me in entries ) {
2011-10-19 05:50:10 +00:00
flatlist . Add ( me );
flatlist . AddRange ( me . Incrementals );
}
//Now remove all but those requested
List < ManifestEntry > toremove = new List < ManifestEntry >();
2012-08-15 18:33:46 +00:00
while ( flatlist . Count > x ) {
2011-10-19 05:50:10 +00:00
toremove . Add ( flatlist [ 0 ]);
flatlist . RemoveAt ( 0 );
}
//If there are still chains left, make sure we do not end up with a partial chain
2012-08-15 18:33:46 +00:00
if (! m_options . AllowFullRemoval || flatlist . Count != 0 ) {
2011-10-19 05:50:10 +00:00
//Go back until we have a full chain
2012-08-15 18:33:46 +00:00
while ( toremove . Count > 0 && ( flatlist . Count == 0 || ! flatlist [ 0 ]. IsFull )) {
2011-10-19 05:50:10 +00:00
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingBackupSetMessage , toremove [ toremove . Count - 1 ]. Time ));
flatlist . Insert ( 0 , toremove [ toremove . Count - 1 ]);
toremove . RemoveAt ( toremove . Count - 1 );
}
}
if ( toremove . Count > 0 && ! m_options . AllowFullRemoval && ( flatlist . Count == 0 || ! flatlist [ 0 ]. IsFull ))
throw new Exception ( Strings . Interface . InternalDeleteCountError );
sb . Append ( RemoveBackupSets ( backend , toremove ));
2012-05-15 17:59:40 +00:00
backend . RecalculateChainMetadata ( flatlist );
2011-10-19 05:50:10 +00:00
}
2012-08-15 18:33:46 +00:00
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
finally
{
if ( MetadataReport != null && backend != null )
MetadataReport ( backend . Metadata . AsReport ());
2012-05-14 18:48:21 +00:00
2012-08-15 18:33:46 +00:00
if ( OperationCompleted != null )
OperationCompleted ( this , DuplicatiOperation . Remove , stats . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
}
2011-10-19 05:50:10 +00:00
2012-08-15 18:33:46 +00:00
m_result = sb ;
2011-10-19 05:50:10 +00:00
return sb . ToString ();
}
2010-06-19 21:08:21 +00:00
public string DeleteAllButNFull ()
2009-01-17 15:27:51 +00:00
{
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . DeleteAllButNFull );
SetupCommonOptions ( stats );
2010-09-17 16:20:16 +00:00
int x = Math . Max ( 0 , m_options . DeleteAllButNFull );
2009-01-17 15:27:51 +00:00
2010-01-18 22:22:42 +00:00
StringBuilder sb = new StringBuilder ();
2012-08-15 18:33:46 +00:00
BackendWrapper backend = null ;
2010-01-18 22:22:42 +00:00
2009-01-25 19:33:36 +00:00
try
{
2012-08-15 18:33:46 +00:00
using ( backend = new BackendWrapper ( stats , m_backend , m_options ))
{
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . Remove , stats . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-01-20 17:17:46 +00:00
2012-08-15 18:33:46 +00:00
List < ManifestEntry > entries = backend . GetBackupSets ();
List < ManifestEntry > toremove = new List < ManifestEntry >();
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
while ( entries . Count > x )
2010-01-18 22:22:42 +00:00
{
2012-08-15 18:33:46 +00:00
if ( entries . Count == 1 && ! m_options . AllowFullRemoval )
{
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingLastFullMessage , entries [ 0 ]. Time ));
break ;
}
2010-01-18 22:22:42 +00:00
2012-08-15 18:33:46 +00:00
ManifestEntry be = entries [ 0 ];
entries . RemoveAt ( 0 );
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
be . Incrementals . Reverse ();
toremove . AddRange ( be . Incrementals );
toremove . Add ( be );
}
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
if ( entries . Count == 0 && toremove . Count > 0 && ! m_options . AllowFullRemoval )
throw new Exception ( Strings . Interface . InternalDeleteCountError );
2010-01-18 22:22:42 +00:00
2012-08-15 18:33:46 +00:00
sb . Append ( RemoveBackupSets ( backend , toremove ));
backend . RecalculateChainMetadata ( entries );
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
2009-01-25 19:33:36 +00:00
}
finally
{
2012-05-14 18:48:21 +00:00
if ( MetadataReport != null )
MetadataReport ( backend . Metadata . AsReport ());
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2010-09-26 20:43:16 +00:00
OperationCompleted ( this , DuplicatiOperation . Remove , stats . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-01-25 19:33:36 +00:00
}
2010-01-18 22:22:42 +00:00
2012-08-15 18:33:46 +00:00
m_result = sb ;
2010-01-18 22:22:42 +00:00
return sb . ToString ();
2009-01-14 17:29:30 +00:00
}
2010-06-19 21:08:21 +00:00
public string DeleteOlderThan ()
2009-01-17 15:27:51 +00:00
{
StringBuilder sb = new StringBuilder ();
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . DeleteOlderThan );
SetupCommonOptions ( stats );
2009-01-17 15:27:51 +00:00
2009-03-22 22:55:04 +00:00
DateTime expires = m_options . RemoveOlderThan ;
2012-08-15 18:33:46 +00:00
BackendWrapper backend = null ;
2009-01-25 19:33:36 +00:00
try
{
2012-08-15 18:33:46 +00:00
using ( backend = new BackendWrapper ( stats , m_backend , m_options ))
2009-01-25 19:33:36 +00:00
{
2012-08-15 18:33:46 +00:00
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . Remove , stats . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2010-01-18 22:22:42 +00:00
2012-08-15 18:33:46 +00:00
List < ManifestEntry > entries = backend . GetBackupSets ();
List < ManifestEntry > toremove = new List < ManifestEntry >();
2009-01-25 19:33:36 +00:00
2012-08-15 18:33:46 +00:00
while ( entries . Count > 0 && entries [ 0 ]. Time <= expires )
{
if ( entries . Count == 1 && ! m_options . AllowFullRemoval )
2009-01-25 19:33:36 +00:00
{
2012-08-15 18:33:46 +00:00
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingLastFullMessage , entries [ 0 ]. Time ));
2009-01-25 19:33:36 +00:00
break ;
}
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
ManifestEntry be = entries [ 0 ];
entries . RemoveAt ( 0 );
2009-01-25 19:33:36 +00:00
2012-08-15 18:33:46 +00:00
bool hasNewer = false ;
foreach ( ManifestEntry bex in be . Incrementals )
if ( bex . Time >= expires )
{
hasNewer = true ;
break ;
}
2009-01-25 19:33:36 +00:00
2012-08-15 18:33:46 +00:00
if ( hasNewer )
{
List < ManifestEntry > t = new List < ManifestEntry >( be . Incrementals );
t . Insert ( 0 , be );
for ( int i = 0 ; i < t . Count ; i ++)
if ( t [ i ]. Time <= expires )
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingBackupSetMessage , t [ i ]. Time . ToString ( System . Globalization . CultureInfo . InvariantCulture )));
break ;
}
else
{
be . Incrementals . Reverse ();
toremove . AddRange ( be . Incrementals );
toremove . Add ( be );
}
2009-01-25 19:33:36 +00:00
}
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
if ( entries . Count == 0 && toremove . Count > 0 && ! m_options . AllowFullRemoval )
throw new Exception ( Strings . Interface . InternalDeleteCountError );
2009-01-17 15:27:51 +00:00
2012-08-15 18:33:46 +00:00
sb . Append ( RemoveBackupSets ( backend , toremove ));
backend . RecalculateChainMetadata ( entries );
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
2009-01-25 19:33:36 +00:00
}
finally
{
2012-05-14 18:48:21 +00:00
if ( MetadataReport != null && backend != null )
MetadataReport ( backend . Metadata . AsReport ());
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2010-09-26 20:43:16 +00:00
OperationCompleted ( this , DuplicatiOperation . Remove , stats . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-01-17 15:27:51 +00:00
}
2012-08-15 18:33:46 +00:00
m_result = sb ;
2009-01-17 15:27:51 +00:00
return sb . ToString ();
}
2010-05-12 08:41:54 +00:00
private string RemoveBackupSets ( BackendWrapper backend , List < ManifestEntry > entries )
2009-01-17 15:27:51 +00:00
{
StringBuilder sb = new StringBuilder ();
2011-10-30 11:56:42 +00:00
sb . Append ( backend . FinishDeleteTransaction ( false ));
if ( entries . Count > 0 )
2009-01-17 15:27:51 +00:00
{
2011-10-30 11:56:42 +00:00
System . Xml . XmlDocument doc = new System . Xml . XmlDocument ();
System . Xml . XmlNode root = doc . AppendChild ( doc . CreateElement ( "files" ));
root . Attributes . Append ( doc . CreateAttribute ( "version" )). Value = "1" ;
2009-01-17 15:27:51 +00:00
2011-10-30 11:56:42 +00:00
foreach ( ManifestEntry me in entries )
2009-01-17 15:27:51 +00:00
{
2010-08-19 16:53:05 +00:00
if ( me . Alternate != null )
2011-10-30 11:56:42 +00:00
root . AppendChild ( doc . CreateElement ( "file" )). InnerText = me . Alternate . Filename ;
root . AppendChild ( doc . CreateElement ( "file" )). InnerText = me . Filename ;
2009-01-17 15:27:51 +00:00
2011-03-10 18:26:38 +00:00
if ( me . Verification != null )
2011-10-30 11:56:42 +00:00
root . AppendChild ( doc . CreateElement ( "file" )). InnerText = me . Verification . Filename ;
2011-03-10 18:26:38 +00:00
2010-05-12 08:41:54 +00:00
foreach ( KeyValuePair < SignatureEntry , ContentEntry > kx in me . Volumes )
{
2011-10-30 11:56:42 +00:00
root . AppendChild ( doc . CreateElement ( "file" )). InnerText = kx . Key . Filename ;
root . AppendChild ( doc . CreateElement ( "file" )). InnerText = kx . Value . Filename ;
2010-05-12 08:41:54 +00:00
}
2009-01-17 15:27:51 +00:00
}
2011-10-30 11:56:42 +00:00
if ( m_options . Force )
{
using ( TempFile tf = new TempFile ())
{
doc . Save ( tf );
tf . Protected = true ;
backend . WriteDeleteTransactionFile ( tf );
}
}
foreach ( ManifestEntry me in entries )
{
sb . AppendLine ( string . Format ( Strings . Interface . DeletingBackupSetMessage , me . Time . ToString ( System . Globalization . CultureInfo . InvariantCulture )));
if ( m_options . Force )
{
//Delete manifest
if ( me . Alternate != null )
backend . Delete ( me . Alternate );
backend . Delete ( me );
if ( me . Verification != null )
backend . Delete ( me . Verification );
foreach ( KeyValuePair < SignatureEntry , ContentEntry > kx in me . Volumes )
{
backend . Delete ( kx . Key );
backend . Delete ( kx . Value );
2012-05-15 17:59:40 +00:00
backend . Metadata . TotalVolumeCount --;
2011-10-30 11:56:42 +00:00
}
2012-05-15 17:59:40 +00:00
if ( me . IsFull )
backend . Metadata . FullBackupCount --;
backend . Metadata . TotalBackupSets --;
2011-10-30 11:56:42 +00:00
}
}
if ( m_options . Force )
backend . RemoveDeleteTransactionFile ();
if (! m_options . Force && entries . Count > 0 )
sb . AppendLine ( Strings . Interface . FilesAreNotForceDeletedMessage );
}
2009-01-17 15:27:51 +00:00
return sb . ToString ();
}
2009-02-15 23:27:14 +00:00
public string [] List ()
2009-01-14 17:29:30 +00:00
{
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . List );
SetupCommonOptions ( stats );
2009-01-14 17:29:30 +00:00
2012-08-15 18:33:46 +00:00
try
2010-09-17 16:20:16 +00:00
{
2012-08-15 18:33:46 +00:00
List < string > res = new List < string >();
2009-01-25 19:33:36 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
{
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . List , stats . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-01-14 17:29:30 +00:00
2012-08-15 18:33:46 +00:00
foreach ( Duplicati . Library . Interface . IFileEntry fe in backend . List ( false ))
res . Add ( fe . Name );
if ( OperationCompleted != null )
OperationCompleted ( this , DuplicatiOperation . List , stats . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-02-15 23:27:14 +00:00
2012-08-15 18:33:46 +00:00
m_result = res ;
return res . ToArray ();
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
2010-09-17 16:20:16 +00:00
}
2012-08-15 18:33:46 +00:00
2009-01-14 17:29:30 +00:00
}
2009-03-21 12:46:54 +00:00
public string Cleanup ()
{
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . CleanUp );
SetupCommonOptions ( stats );
2010-09-17 16:20:16 +00:00
2009-03-21 12:46:54 +00:00
bool anyRemoved = false ;
2011-10-30 11:56:42 +00:00
StringBuilder sb = new StringBuilder ();
2012-08-15 18:33:46 +00:00
try
2009-03-21 12:46:54 +00:00
{
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
{
List < ManifestEntry > sorted = backend . GetBackupSets ();
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
List < ManifestEntry > entries = new List < ManifestEntry >();
entries . AddRange ( sorted );
foreach ( ManifestEntry be in sorted )
entries . AddRange ( be . Incrementals );
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
string cleanup = backend . DeleteOrphans ( false );
if (! string . IsNullOrEmpty ( cleanup ))
sb . AppendLine ( cleanup );
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
if ( m_options . SkipFileHashChecks )
throw new Exception ( Strings . Interface . CannotCleanWithoutHashesError );
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . CannotCleanWithoutHashesError );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
//We need the manifests anyway, so we verify the chain
if ( entries . Count > 0 )
VerifyManifestChain ( backend , entries [ 0 ]);
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
//Now compare the actual filelist with the manifest
foreach ( ManifestEntry be in entries )
{
Manifestfile manifest = GetManifest ( backend , be );
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
int count = manifest . ContentHashes . Count ;
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
for ( int i = count - 1 ; i < be . Volumes . Count ; i ++)
{
anyRemoved = true ;
2011-10-30 11:56:42 +00:00
2012-08-15 18:33:46 +00:00
string sigmsg = string . Format ( Strings . Interface . RemovingPartialFilesMessage , be . Volumes [ i ]. Key . Filename );
string cntmsg = string . Format ( Strings . Interface . RemovingPartialFilesMessage , be . Volumes [ i ]. Value . Filename );
2011-10-30 11:56:42 +00:00
2012-08-15 18:33:46 +00:00
Logging . Log . WriteMessage ( sigmsg , Duplicati . Library . Logging . LogMessageType . Information );
Logging . Log . WriteMessage ( cntmsg , Duplicati . Library . Logging . LogMessageType . Information );
2011-10-30 11:56:42 +00:00
2012-08-15 18:33:46 +00:00
sb . AppendLine ( sigmsg );
sb . AppendLine ( cntmsg );
2011-10-30 11:56:42 +00:00
2012-08-15 18:33:46 +00:00
if ( m_options . Force )
{
backend . Delete ( be . Volumes [ i ]. Key );
backend . Delete ( be . Volumes [ i ]. Value );
}
2009-03-21 12:46:54 +00:00
}
}
}
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2009-03-21 12:46:54 +00:00
2009-03-22 22:55:04 +00:00
if (! m_options . Force && anyRemoved )
2011-10-30 11:56:42 +00:00
{
2009-06-14 21:51:06 +00:00
Logging . Log . WriteMessage ( Strings . Interface . FilesAreNotForceDeletedMessage , Duplicati . Library . Logging . LogMessageType . Information );
2011-10-30 11:56:42 +00:00
sb . AppendLine ( Strings . Interface . FilesAreNotForceDeletedMessage );
}
2009-03-21 12:46:54 +00:00
2012-08-15 18:33:46 +00:00
m_result = sb ;
2011-10-30 11:56:42 +00:00
return sb . ToString (); //TODO: Write a message here?
2009-03-21 12:46:54 +00:00
}
2012-08-15 18:33:46 +00:00
public IList < string > ListCurrentFiles ()
2009-02-09 06:47:00 +00:00
{
2010-09-26 20:43:16 +00:00
RestoreStatistics rs = new RestoreStatistics ( DuplicatiOperationMode . ListCurrentFiles );
SetupCommonOptions ( rs );
2009-02-09 06:47:00 +00:00
2009-03-21 12:46:54 +00:00
List < string > res ;
2012-08-15 18:33:46 +00:00
try
2009-02-09 06:47:00 +00:00
{
2012-08-15 18:33:46 +00:00
Utility . FilenameFilter filter = m_options . Filter ;
DateTime timelimit = m_options . RestoreTime ;
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . List , rs . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-03-26 19:39:50 +00:00
2009-02-09 06:47:00 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( rs , m_backend , m_options ))
using ( Utility . TempFolder basefolder = new Duplicati . Library . Utility . TempFolder ()) {
ManifestEntry bestFit = backend . GetBackupSet ( timelimit );
List < ManifestEntry > entries = new List < ManifestEntry >();
entries . Add ( bestFit );
entries . AddRange ( bestFit . Incrementals );
2009-02-09 06:47:00 +00:00
2012-08-15 18:33:46 +00:00
List < KeyValuePair < ManifestEntry , CompressionWrapper >> patches = FindPatches ( backend , entries , basefolder , false , rs );
2009-02-09 06:47:00 +00:00
2012-08-15 18:33:46 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( new string [] { basefolder }, rs , backend . Metadata , filter , patches ))
res = dir . UnmatchedFiles ();
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
finally
{
if ( OperationCompleted != null )
OperationCompleted ( this , DuplicatiOperation . List , rs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
}
m_result = res ;
2009-03-21 12:46:54 +00:00
return res ;
2009-02-09 06:47:00 +00:00
}
2012-08-15 18:33:46 +00:00
public string [] ListSourceFolders ()
2010-05-12 08:41:54 +00:00
{
2010-09-26 20:43:16 +00:00
RestoreStatistics rs = new RestoreStatistics ( DuplicatiOperationMode . ListSourceFolders );
SetupCommonOptions ( rs );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
string [] res ;
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
try
{
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . ManifestsMustBeRead );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
DateTime timelimit = m_options . RestoreTime ;
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . List , rs . OperationMode , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( rs , m_backend , m_options ))
using ( Utility . TempFile mfile = new Duplicati . Library . Utility . TempFile ()) {
ManifestEntry bestFit = backend . GetBackupSet ( timelimit );
2010-05-12 08:41:54 +00:00
2012-08-15 18:33:46 +00:00
backend . Get ( bestFit , null , mfile , null );
res = new Manifestfile ( mfile , m_options . SkipFileHashChecks ). SourceDirs ;
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
finally
{
if ( OperationCompleted != null )
OperationCompleted ( this , DuplicatiOperation . List , rs . OperationMode , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2010-05-12 08:41:54 +00:00
}
2012-08-15 18:33:46 +00:00
m_result = res ;
2010-05-12 08:41:54 +00:00
return res ;
}
2010-09-26 20:43:16 +00:00
private void SetupCommonOptions ( CommunicationStatistics stats )
2012-08-15 18:33:46 +00:00
{
string [] tmp = null ;
SetupCommonOptions ( stats , ref tmp );
}
private void SetupCommonOptions ( CommunicationStatistics stats , ref string [] paths )
{
2010-09-26 20:43:16 +00:00
m_options . MainAction = stats . OperationMode ;
2012-05-08 19:00:53 +00:00
switch ( m_options . MainAction )
{
case DuplicatiOperationMode . Backup :
case DuplicatiOperationMode . BackupFull :
case DuplicatiOperationMode . BackupIncremental :
break ;
default :
//It only makes sense to enable auto-creation if we are writing files.
if (! m_options . RawOptions . ContainsKey ( "disable-autocreate-folder" ))
m_options . RawOptions [ "disable-autocreate-folder" ] = "true" ;
break ;
}
2010-09-17 16:20:16 +00:00
2012-08-15 18:33:46 +00:00
//Load all generic modules
m_options . LoadedModules . Clear ();
foreach ( Library . Interface . IGenericModule m in DynamicLoader . GenericLoader . Modules )
m_options . LoadedModules . Add ( new KeyValuePair < bool , Library . Interface . IGenericModule >( Array . IndexOf < string >( m_options . DisableModules , m . Key . ToLower ()) < 0 && ( m . LoadAsDefault || Array . IndexOf < string >( m_options . EnableModules , m . Key . ToLower ()) >= 0 ), m ));
foreach ( KeyValuePair < bool , Library . Interface . IGenericModule > mx in m_options . LoadedModules )
if ( mx . Key )
{
mx . Value . Configure ( m_options . RawOptions );
if ( mx . Value is Library . Interface . IGenericCallbackModule )
(( Library . Interface . IGenericCallbackModule ) mx . Value ). OnStart ( stats . OperationMode . ToString (), ref m_backend , ref paths );
}
2010-10-09 15:43:03 +00:00
2011-06-17 13:31:28 +00:00
OperationRunning ( true );
2012-08-15 18:33:46 +00:00
Library . Logging . Log . LogLevel = m_options . Loglevel ;
2010-10-09 15:43:03 +00:00
if (! string . IsNullOrEmpty ( m_options . Logfile ))
2012-04-11 19:41:13 +00:00
{
m_hasSetLogging = true ;
2010-10-09 15:43:03 +00:00
Library . Logging . Log . CurrentLog = new Library . Logging . StreamLog ( m_options . Logfile );
2012-04-11 19:41:13 +00:00
}
2010-10-09 15:43:03 +00:00
2010-09-11 13:52:30 +00:00
if ( stats != null )
2011-10-28 05:45:08 +00:00
{
2010-09-11 13:52:30 +00:00
stats . VerboseErrors = m_options . DebugOutput ;
2011-10-28 05:45:08 +00:00
stats . VerboseRetryErrors = m_options . VerboseRetryErrors ;
}
2010-09-11 13:52:30 +00:00
2009-03-22 22:55:04 +00:00
if (! string . IsNullOrEmpty ( m_options . TempDir ))
2010-11-21 11:08:15 +00:00
Utility . TempFolder . SystemTempPath = m_options . TempDir ;
2009-03-22 22:55:04 +00:00
if (! string . IsNullOrEmpty ( m_options . ThreadPriority ))
2010-11-21 11:08:15 +00:00
System . Threading . Thread . CurrentThread . Priority = Utility . Utility . ParsePriority ( m_options . ThreadPriority );
2010-06-19 21:08:21 +00:00
2010-08-12 18:32:15 +00:00
ValidateOptions ( stats );
2012-06-29 19:38:04 +00:00
Library . Logging . Log . WriteMessage ( string . Format ( Strings . Interface . StartingOperationMessage , m_options . MainAction ), Logging . LogMessageType . Information );
2009-01-14 17:29:30 +00:00
}
2009-02-15 23:27:14 +00:00
2010-05-12 08:41:54 +00:00
/// <summary>
/// Downloads all required signature files from the backend.
/// </summary>
/// <param name="backend">The backend to read from</param>
/// <param name="entries">The flattened list of manifests</param>
/// <param name="tempfolder">The tempfolder set for this operation</param>
2010-09-04 12:23:03 +00:00
/// <param name="allowHashFail">True to ignore files with failed hash signature</param>
2010-05-12 08:41:54 +00:00
/// <returns>A list of file archives</returns>
2012-08-07 17:42:09 +00:00
private List < KeyValuePair < ManifestEntry , CompressionWrapper >> FindPatches ( BackendWrapper backend , List < ManifestEntry > entries , string tempfolder , bool allowHashFail , CommunicationStatistics stat )
2009-03-21 12:46:54 +00:00
{
2012-08-07 17:42:09 +00:00
List < KeyValuePair < ManifestEntry , CompressionWrapper >> patches = new List < KeyValuePair < ManifestEntry , CompressionWrapper >>();
2009-03-21 12:46:54 +00:00
using ( new Logging . Timer ( "Reading incremental data" ))
{
2010-09-26 20:43:16 +00:00
OperationProgress ( this , GetOperationType (), stat . OperationMode , ( int )( m_progress * 100 ), - 1 , Strings . Interface . StatusReadingIncrementalData , "" );
2009-03-21 12:46:54 +00:00
//Calculate the total number of files to download
//, and verify their order
int incCount = 0 ;
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2009-03-21 12:46:54 +00:00
{
int volNo = 0 ;
//Prevent order based bugs
if ( entries . IndexOf ( be ) > 0 )
if ( entries [ entries . IndexOf ( be ) - 1 ]. Time >= be . Time )
2009-06-14 21:51:06 +00:00
throw new Exception ( Strings . Interface . BadSortingDetectedError );
2009-03-21 12:46:54 +00:00
incCount ++;
2010-05-12 08:41:54 +00:00
foreach ( KeyValuePair < SignatureEntry , ContentEntry > bes in be . Volumes )
2009-03-21 12:46:54 +00:00
{
incCount ++;
2010-05-12 08:41:54 +00:00
if ( volNo + 1 != bes . Key . Volumenumber || bes . Key . Volumenumber != bes . Value . Volumenumber )
2009-06-14 21:51:06 +00:00
throw new Exception ( Strings . Interface . BadVolumeSortOrder );
2009-03-21 12:46:54 +00:00
volNo ++;
}
}
//The incremental part has a fixed cost, and each file has a fixed fraction of that
double unitCost = m_incrementalFraction / incCount ;
2011-03-10 18:26:38 +00:00
//Ensure that the manifest chain has not been tampered with
// since we will read all manifest files anyway, there is no harm in doing it here
if (! m_options . DontReadManifests && entries . Count > 0 )
VerifyManifestChain ( backend , entries [ entries . Count - 1 ]);
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2009-03-21 12:46:54 +00:00
{
m_progress += unitCost ;
2010-05-12 08:41:54 +00:00
Manifestfile manifest = GetManifest ( backend , be );
2009-03-21 12:46:54 +00:00
2010-05-12 08:41:54 +00:00
foreach ( KeyValuePair < SignatureEntry , ContentEntry > bes in be . Volumes )
2009-03-21 12:46:54 +00:00
{
m_progress += unitCost ;
//Skip non-listed incrementals
2010-05-12 08:41:54 +00:00
if ( manifest . SignatureHashes != null && bes . Key . Volumenumber > manifest . SignatureHashes . Count )
2009-03-21 12:46:54 +00:00
{
2010-05-12 08:41:54 +00:00
backend . AddOrphan ( bes . Key );
backend . AddOrphan ( bes . Value );
2009-03-21 12:46:54 +00:00
continue ;
}
2010-09-26 20:43:16 +00:00
OperationProgress ( this , GetOperationType (), stat . OperationMode , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusReadingSignatureFile , be . Time . ToShortDateString () + " " + be . Time . ToShortTimeString (), bes . Key . Volumenumber ), "" );
2009-03-21 12:46:54 +00:00
string filename = System . IO . Path . Combine ( tempfolder , "patch-" + patches . Count . ToString () + ".zip" );
2010-09-01 21:04:18 +00:00
//Check just before we download stuff
CheckLiveControl ();
2010-09-04 12:23:03 +00:00
try
{
using ( new Logging . Timer ( "Get " + bes . Key . Filename ))
2011-03-10 18:26:38 +00:00
backend . Get ( bes . Key , manifest , filename , manifest . SignatureHashes == null ? null : manifest . SignatureHashes [ bes . Key . Volumenumber - 1 ]);
2010-09-04 12:23:03 +00:00
}
catch ( BackendWrapper . HashMismathcException hme )
{
if ( allowHashFail )
{
if ( stat != null )
2010-09-11 12:43:55 +00:00
stat . LogError ( string . Format ( Strings . Interface . FileHashFailure , hme . Message ), hme );
2010-09-04 12:23:03 +00:00
continue ;
}
else
throw ;
}
2009-03-21 12:46:54 +00:00
2012-08-07 17:42:09 +00:00
patches . Add ( new KeyValuePair < ManifestEntry , CompressionWrapper >( be , CompressionWrapper . GetModule ( bes . Key . Compression , filename , m_options . RawOptions )));
2009-03-21 12:46:54 +00:00
}
}
}
2011-03-10 18:26:38 +00:00
backend . DeleteOrphans ( true );
2009-03-21 12:46:54 +00:00
return patches ;
}
2009-03-23 20:44:25 +00:00
public void PurgeSignatureCache ()
{
if ( string . IsNullOrEmpty ( m_options . SignatureCachePath ))
2009-06-14 21:51:06 +00:00
throw new Exception ( Strings . Interface . SignatureCachePathMissingError );
2009-03-23 20:44:25 +00:00
else
2009-04-01 08:30:14 +00:00
RemoveSignatureFiles ( m_options . SignatureCachePath );
2009-03-23 20:44:25 +00:00
}
2010-05-12 08:41:54 +00:00
public List < ManifestEntry > GetBackupSets ()
2009-03-26 19:39:50 +00:00
{
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . GetBackupSets );
SetupCommonOptions ( stats );
2010-09-17 16:20:16 +00:00
2012-08-15 18:33:46 +00:00
try
{
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
{
List < ManifestEntry > res = backend . GetBackupSets ();
m_result = res ;
return res ;
}
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2009-03-26 19:39:50 +00:00
}
2009-03-29 21:59:00 +00:00
public List < KeyValuePair < RSync . RSyncDir . PatchFileType , string >> ListActualSignatureFiles ()
{
2010-09-26 20:43:16 +00:00
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . ListActualSignatureFiles );
SetupCommonOptions ( stats );
2010-09-17 16:20:16 +00:00
2012-08-15 18:33:46 +00:00
try
2009-03-29 21:59:00 +00:00
{
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
2009-03-29 21:59:00 +00:00
{
2012-08-15 18:33:46 +00:00
ManifestEntry bestFit = backend . GetBackupSet ( m_options . RestoreTime );
if ( bestFit . Incrementals . Count > 0 ) //Get the most recent incremental
bestFit = bestFit . Incrementals [ bestFit . Incrementals . Count - 1 ];
using ( Utility . TempFolder folder = new Duplicati . Library . Utility . TempFolder ())
{
List < CompressionWrapper > patches = new List < CompressionWrapper >();
foreach ( KeyValuePair < ManifestEntry , CompressionWrapper > entry in FindPatches ( backend , new List < ManifestEntry >( new ManifestEntry [] { bestFit }), folder , false , stats ))
patches . Add ( entry . Value );
2010-10-23 08:58:24 +00:00
2012-08-15 18:33:46 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( new string [] { folder }, stats , backend . Metadata , null ))
{
List < KeyValuePair < RSync . RSyncDir . PatchFileType , string >> res = dir . ListPatchFiles ( patches );
m_result = res ;
return res ;
}
}
2009-03-29 21:59:00 +00:00
}
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2009-03-29 21:59:00 +00:00
}
2009-04-01 08:30:14 +00:00
2012-08-15 18:33:46 +00:00
public void CreateFolder ()
2010-10-23 11:40:43 +00:00
{
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . CreateFolder );
SetupCommonOptions ( stats );
2012-08-15 18:33:46 +00:00
try
{
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
backend . CreateFolder ();
m_result = "" ;
}
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2010-10-23 11:40:43 +00:00
}
2011-03-10 18:26:38 +00:00
public List < KeyValuePair < BackupEntryBase , Exception >> VerifyBackupChain ()
{
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . Verify );
SetupCommonOptions ( stats );
List < KeyValuePair < BackupEntryBase , Exception >> results = new List < KeyValuePair < BackupEntryBase , Exception >>();
2012-08-15 18:33:46 +00:00
try
2011-03-10 18:26:38 +00:00
{
2012-08-15 18:33:46 +00:00
if ( m_options . DontReadManifests )
throw new InvalidOperationException ( Strings . Interface . ManifestsMustBeRead );
if ( m_options . SkipFileHashChecks )
throw new InvalidOperationException ( Strings . Interface . CannotVerifyWithoutHashes );
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
if (! string . IsNullOrEmpty ( m_options . SignatureCachePath ))
{
stats . LogWarning ( Strings . Interface . DisablingSignatureCacheForVerification , null );
m_options . SignatureCachePath = null ;
}
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
{
//Find the spot in the chain where we start
ManifestEntry bestFit = backend . GetBackupSet ( m_options . RestoreTime );
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
//Get the list of manifests to validate
List < ManifestEntry > entries = new List < ManifestEntry >();
entries . Add ( bestFit );
entries . AddRange ( bestFit . Incrementals );
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
entries . Reverse ();
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
foreach ( ManifestEntry me in entries )
2011-03-10 18:26:38 +00:00
{
2012-08-15 18:33:46 +00:00
Manifestfile mf = null ;
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
try
{
mf = GetManifest ( backend , me );
VerifyBackupChainWithFiles ( backend , me );
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
if ( mf . SignatureHashes . Count != me . Volumes . Count )
results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me , new Exception ( string . Format ( Strings . Interface . ManifestAndFileCountMismatchError , mf . SelfFilename , mf . SignatureHashes . Count , me . Volumes . Count ))));
else
results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me , null ));
}
catch ( Exception ex )
2011-03-10 18:26:38 +00:00
{
2012-08-15 18:33:46 +00:00
results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me , ex ));
}
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
if ( mf != null )
{
int volumes = Math . Min ( mf . SignatureHashes . Count , me . Volumes . Count );
for ( int i = 0 ; i < volumes ; i ++)
2011-03-10 18:26:38 +00:00
{
2012-08-15 18:33:46 +00:00
if ( m_options . Verificationlevel == VerificationLevel . Signature || m_options . Verificationlevel == VerificationLevel . Full )
{
try
{
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
backend . Get ( me . Volumes [ i ]. Key , mf , tf , mf . SignatureHashes [ i ]);
results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me . Volumes [ i ]. Key , null ));
}
catch ( Exception ex ) { results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me . Volumes [ i ]. Key , ex )); }
2011-03-10 18:26:38 +00:00
}
2012-08-15 18:33:46 +00:00
if ( m_options . Verificationlevel == VerificationLevel . Full )
{
try
{
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
backend . Get ( me . Volumes [ i ]. Value , mf , tf , mf . ContentHashes [ i ]);
results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me . Volumes [ i ]. Value , null ));
}
catch ( Exception ex ) { results . Add ( new KeyValuePair < BackupEntryBase , Exception >( me . Volumes [ i ]. Value , ex )); }
}
2011-03-10 18:26:38 +00:00
}
}
2012-08-15 18:33:46 +00:00
}
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
//Re-generate verification file
if ( m_options . CreateVerificationFile )
2011-03-10 18:26:38 +00:00
{
2012-08-15 18:33:46 +00:00
//Stop any async operations
if ( m_options . AsynchronousUpload )
backend . ExtractPendingUploads ();
VerificationFile vf = new VerificationFile ( entries , new FilenameStrategy ( m_options ));
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
{
vf . Save ( tf );
tf . Protected = true ;
backend . Put ( new VerificationEntry ( entries [ entries . Count - 1 ]. Time ), tf );
}
2011-03-10 18:26:38 +00:00
}
2012-08-15 18:33:46 +00:00
}
2011-03-10 18:26:38 +00:00
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2011-03-10 18:26:38 +00:00
2012-08-15 18:33:46 +00:00
m_result = results ;
2011-03-10 18:26:38 +00:00
return results ;
}
2010-10-23 11:40:43 +00:00
2010-10-23 13:10:54 +00:00
/// <summary>
/// Reads through a backup and finds the last backup entry that has a specific file
/// </summary>
/// <returns></returns>
public List < KeyValuePair < string , DateTime >> FindLastFileVersion ()
{
CommunicationStatistics stats = new CommunicationStatistics ( DuplicatiOperationMode . FindLastFileVersion );
SetupCommonOptions ( stats );
2012-08-15 18:33:46 +00:00
try
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . ManifestsMustBeRead );
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
if ( string . IsNullOrEmpty ( m_options . FileToRestore ))
throw new Exception ( Strings . Interface . NoFilesGivenError );
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
string [] filesToFind = m_options . FileToRestore . Split ( System . IO . Path . PathSeparator );
KeyValuePair < string , DateTime >[] results = new KeyValuePair < string , DateTime >[ filesToFind . Length ];
for ( int i = 0 ; i < results . Length ; i ++)
results [ i ] = new KeyValuePair < string , DateTime >( filesToFind [ i ], new DateTime ( 0 ));
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
using ( BackendWrapper backend = new BackendWrapper ( stats , m_backend , m_options ))
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
//Extract the full backup set list
List < ManifestEntry > fulls = backend . GetBackupSets ();
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
//Flatten the list
List < ManifestEntry > workList = new List < ManifestEntry >();
//The list is oldest first, this function work newest first
fulls . Reverse ();
foreach ( ManifestEntry f in fulls )
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
f . Incrementals . Reverse ();
workList . AddRange ( f . Incrementals );
workList . Add ( f );
2010-10-23 13:10:54 +00:00
}
2012-08-15 18:33:46 +00:00
bool warned_manifest_v1 = false ;
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
foreach ( ManifestEntry mf in workList )
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
List < Manifestfile . HashEntry > signatureHashes = null ;
Manifestfile mfi ;
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
backend . Get ( mf , null , tf , null );
mfi = new Manifestfile ( tf , m_options . SkipFileHashChecks );
if (! m_options . SkipFileHashChecks )
signatureHashes = mfi . SignatureHashes ;
}
//If there are no volumes, don't stop here
bool any_unmatched = true ;
if ( stats != null && ! warned_manifest_v1 && ( mfi . SourceDirs == null || mfi . SourceDirs . Length == 0 ))
{
warned_manifest_v1 = true ;
stats . LogWarning ( Strings . Interface . ManifestVersionRequiresRelativeNamesWarning , null );
}
foreach ( KeyValuePair < SignatureEntry , ContentEntry > e in mf . Volumes )
using ( Utility . TempFile tf = new Duplicati . Library . Utility . TempFile ())
2010-10-23 13:10:54 +00:00
{
2012-08-15 18:33:46 +00:00
//Skip non-approved signature files
if ( signatureHashes != null && e . Key . Volumenumber > signatureHashes . Count )
{
stats . LogWarning ( string . Format ( Strings . Interface . SkippedUnlistedSignatureFileWarning , e . Key . Filename ), null );
continue ;
}
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
backend . Get ( e . Key , mfi , tf , signatureHashes == null ? null : signatureHashes [ e . Key . Volumenumber - 1 ]);
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
any_unmatched = false ;
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
RSync . RSyncDir . ContainsFile ( mfi , filesToFind , CompressionWrapper . GetModule ( e . Key . Compression , tf , m_options . RawOptions ));
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
for ( int i = 0 ; i < filesToFind . Length ; i ++)
{
if ( results [ i ]. Value . Ticks == 0 && string . IsNullOrEmpty ( filesToFind [ i ]))
results [ i ] = new KeyValuePair < string , DateTime >( results [ i ]. Key , mf . Time );
else
any_unmatched = true ;
}
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
if (! any_unmatched )
break ;
}
2010-10-23 13:10:54 +00:00
2012-08-15 18:33:46 +00:00
if (! any_unmatched )
break ;
}
m_result = results ;
return new List < KeyValuePair < string , DateTime >>( results );
2010-10-23 13:10:54 +00:00
}
}
2012-08-15 18:33:46 +00:00
catch ( Exception ex )
{
m_result = ex ;
throw ;
}
2010-10-23 13:10:54 +00:00
}
2010-08-12 18:32:15 +00:00
/// <summary>
/// This function will examine all options passed on the commandline, and test for unsupported or deprecated values.
/// Any errors will be logged into the statistics module.
/// </summary>
/// <param name="options">The commandline options given</param>
/// <param name="backend">The backend url</param>
/// <param name="stats">The statistics into which warnings are written</param>
private void ValidateOptions ( CommunicationStatistics stats )
{
//No point in going through with this if we can't report
if ( stats == null )
return ;
//Keep a list of all supplied options
Dictionary < string , string > ropts = m_options . RawOptions ;
//Keep a list of all supported options
Dictionary < string , Library . Interface . ICommandLineArgument > supportedOptions = new Dictionary < string , Library . Interface . ICommandLineArgument >();
//There are a few internal options that are not accessible from outside, and thus not listed
foreach ( string s in Options . InternalOptions )
supportedOptions [ s ] = null ;
//Figure out what module options are supported in the current setup
List < Library . Interface . ICommandLineArgument > moduleOptions = new List < Duplicati . Library . Interface . ICommandLineArgument >();
Dictionary < string , string > disabledModuleOptions = new Dictionary < string , string >();
foreach ( KeyValuePair < bool , Library . Interface . IGenericModule > m in m_options . LoadedModules )
if ( m . Value . SupportedCommands != null )
if ( m . Key )
moduleOptions . AddRange ( m . Value . SupportedCommands );
else
{
foreach ( Library . Interface . ICommandLineArgument c in m . Value . SupportedCommands )
{
disabledModuleOptions [ c . Name ] = m . Value . DisplayName + " (" + m . Value . Key + ")" ;
if ( c . Aliases != null )
foreach ( string s in c . Aliases )
disabledModuleOptions [ s ] = disabledModuleOptions [ c . Name ];
}
}
//Now run through all supported options, and look for deprecated options
foreach ( IList < Library . Interface . ICommandLineArgument > l in new IList < Library . Interface . ICommandLineArgument >[] {
m_options . SupportedCommands ,
2011-06-30 07:26:07 +00:00
DynamicLoader . BackendLoader . GetSupportedCommands ( m_backend ),
2010-08-12 18:32:15 +00:00
m_options . NoEncryption ? null : DynamicLoader . EncryptionLoader . GetSupportedCommands ( m_options . EncryptionModule ),
moduleOptions ,
DynamicLoader . CompressionLoader . GetSupportedCommands ( m_options . CompressionModule ) })
{
if ( l != null )
foreach ( Library . Interface . ICommandLineArgument a in l )
{
2010-10-09 15:48:05 +00:00
if ( supportedOptions . ContainsKey ( a . Name ) && Array . IndexOf ( Options . KnownDuplicates , a . Name . ToLower ()) < 0 )
2010-09-11 12:43:55 +00:00
stats . LogWarning ( string . Format ( Strings . Interface . DuplicateOptionNameWarning , a . Name ), null );
2010-08-12 18:32:15 +00:00
supportedOptions [ a . Name ] = a ;
if ( a . Aliases != null )
foreach ( string s in a . Aliases )
{
2010-10-09 15:48:05 +00:00
if ( supportedOptions . ContainsKey ( s ) && Array . IndexOf ( Options . KnownDuplicates , s . ToLower ()) < 0 )
2010-09-11 12:43:55 +00:00
stats . LogWarning ( string . Format ( Strings . Interface . DuplicateOptionNameWarning , s ), null );
2010-08-12 18:32:15 +00:00
supportedOptions [ s ] = a ;
}
if ( a . Deprecated )
{
List < string > aliases = new List < string >();
aliases . Add ( a . Name );
if ( a . Aliases != null )
aliases . AddRange ( a . Aliases );
foreach ( string s in aliases )
if ( ropts . ContainsKey ( s ))
{
string optname = a . Name ;
if ( a . Name != s )
optname += " (" + s + ")" ;
2010-09-11 12:43:55 +00:00
stats . LogWarning ( string . Format ( Strings . Interface . DeprecatedOptionUsedWarning , optname , a . DeprecationMessage ), null );
2010-08-12 18:32:15 +00:00
}
}
}
}
//Now look for options that were supplied but not supported
foreach ( string s in ropts . Keys )
if (! supportedOptions . ContainsKey ( s ))
if ( disabledModuleOptions . ContainsKey ( s ))
2010-09-11 12:43:55 +00:00
stats . LogWarning ( string . Format ( Strings . Interface . UnsupportedOptionDisabledModuleWarning , s , disabledModuleOptions [ s ]), null );
2010-08-12 18:32:15 +00:00
else
2010-09-11 12:43:55 +00:00
stats . LogWarning ( string . Format ( Strings . Interface . UnsupportedOptionWarning , s ), null );
2010-08-12 18:32:15 +00:00
//Look at the value supplied for each argument and see if is valid according to its type
foreach ( string s in ropts . Keys )
{
Library . Interface . ICommandLineArgument arg ;
if ( supportedOptions . TryGetValue ( s , out arg ) && arg != null )
{
2010-08-15 15:41:17 +00:00
string validationMessage = ValidateOptionValue ( arg , s , ropts [ s ]);
if ( validationMessage != null )
2010-09-11 12:43:55 +00:00
stats . LogWarning ( validationMessage , null );
2010-08-15 15:41:17 +00:00
}
}
2012-05-08 18:24:40 +00:00
//TODO: Based on the action, see if all options are relevant
2010-08-15 15:41:17 +00:00
}
2010-08-12 18:32:15 +00:00
2010-08-15 15:41:17 +00:00
#region Static interface
2010-08-12 18:32:15 +00:00
2010-08-15 15:41:17 +00:00
/// <summary>
/// Checks if the value passed to an option is actually valid.
/// </summary>
/// <param name="arg">The argument being validated</param>
/// <param name="optionname">The name of the option to validate</param>
/// <param name="value">The value to check</param>
/// <returns>Null if no errors are found, an error message otherwise</returns>
public static string ValidateOptionValue ( Library . Interface . ICommandLineArgument arg , string optionname , string value )
{
if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Enumeration )
{
bool found = false ;
foreach ( string v in arg . ValidValues ?? new string [ 0 ])
if ( string . Equals ( v , value , StringComparison . CurrentCultureIgnoreCase ))
2010-08-12 18:32:15 +00:00
{
2010-08-15 15:41:17 +00:00
found = true ;
break ;
2010-08-12 18:32:15 +00:00
}
2010-08-15 15:41:17 +00:00
if (! found )
return string . Format ( Strings . Interface . UnsupportedEnumerationValue , optionname , value , string . Join ( ", " , arg . ValidValues ?? new string [ 0 ]));
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Boolean )
{
2010-11-21 11:08:15 +00:00
if (! string . IsNullOrEmpty ( value ) && Utility . Utility . ParseBool ( value , true ) != Utility . Utility . ParseBool ( value , false ))
2010-08-15 15:41:17 +00:00
return string . Format ( Strings . Interface . UnsupportedBooleanValue , optionname , value );
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Integer )
{
long l ;
if (! long . TryParse ( value , out l ))
return string . Format ( Strings . Interface . UnsupportedIntegerValue , optionname , value );
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Path )
{
foreach ( string p in value . Split ( System . IO . Path . DirectorySeparatorChar ))
if ( p . IndexOfAny ( System . IO . Path . GetInvalidPathChars ()) >= 0 )
return string . Format ( Strings . Interface . UnsupportedPathValue , optionname , p );
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Size )
{
try
{
2010-11-21 11:08:15 +00:00
Utility . Sizeparser . ParseSize ( value );
2010-08-15 15:41:17 +00:00
}
catch
{
return string . Format ( Strings . Interface . UnsupportedSizeValue , optionname , value );
}
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Timespan )
{
try
{
2010-11-21 11:08:15 +00:00
Utility . Timeparser . ParseTimeSpan ( value );
2010-08-15 15:41:17 +00:00
}
catch
{
return string . Format ( Strings . Interface . UnsupportedTimeValue , optionname , value );
2010-08-12 18:32:15 +00:00
}
}
2010-08-15 15:41:17 +00:00
return null ;
2010-08-12 18:32:15 +00:00
}
2009-04-01 08:30:14 +00:00
public static void RemoveSignatureFiles ( string folder )
{
FilenameStrategy cachenames = BackendWrapper . CreateCacheFilenameStrategy ();
2010-11-21 11:08:15 +00:00
foreach ( string s in Utility . Utility . EnumerateFiles ( folder ))
2009-04-01 08:30:14 +00:00
{
2010-06-19 21:08:21 +00:00
BackupEntryBase e = cachenames . ParseFilename ( new Duplicati . Library . Interface . FileEntry ( System . IO . Path . GetFileName ( s )));
2010-05-12 08:41:54 +00:00
if ( e is SignatureEntry )
2009-04-01 08:30:14 +00:00
{
try { System . IO . File . Delete ( s ); }
catch {}
}
}
}
2009-03-23 20:44:25 +00:00
2009-02-15 23:27:14 +00:00
//Static helpers
public static string [] List ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . List ();
}
2010-05-12 08:41:54 +00:00
public static string Backup ( string [] source , string target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( target , options ))
return i . Backup ( source );
}
2010-05-12 08:41:54 +00:00
public static string Restore ( string source , string [] target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( source , options ))
return i . Restore ( target );
}
2010-05-12 08:41:54 +00:00
/// <summary>
/// Returns a list of full backup manifests, with a list of volumes and an incremental chain
/// </summary>
/// <param name="target">The backend to read the data from</param>
/// <param name="options">A list of options</param>
/// <returns>A list of full backup manifests</returns>
public static List < ManifestEntry > ParseFileList ( string target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( target , options ))
2009-03-26 19:39:50 +00:00
return i . GetBackupSets ();
2009-02-15 23:27:14 +00:00
}
2010-09-17 16:20:16 +00:00
public static IList < string > ListCurrentFiles ( string target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( target , options ))
2010-09-17 16:20:16 +00:00
return i . ListCurrentFiles ();
2009-02-15 23:27:14 +00:00
}
2010-05-12 08:41:54 +00:00
public static string [] ListSourceFolders ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . ListSourceFolders ();
}
2010-06-19 21:08:21 +00:00
public static string DeleteAllButNFull ( string target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( target , options ))
2010-06-19 21:08:21 +00:00
return i . DeleteAllButNFull ();
2009-02-15 23:27:14 +00:00
}
2011-10-19 05:50:10 +00:00
public static string DeleteAllButN ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . DeleteAllButN ();
}
2010-06-19 21:08:21 +00:00
public static string DeleteOlderThan ( string target , Dictionary < string , string > options )
2009-02-15 23:27:14 +00:00
{
using ( Interface i = new Interface ( target , options ))
2010-06-19 21:08:21 +00:00
return i . DeleteOlderThan ();
2009-02-15 23:27:14 +00:00
}
2009-03-21 12:46:54 +00:00
public static string Cleanup ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . Cleanup ();
}
2009-02-15 23:27:14 +00:00
public static string RestoreControlFiles ( string source , string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( source , options ))
return i . RestoreControlFiles ( target );
}
2009-03-23 20:44:25 +00:00
public static void PurgeSignatureCache ( Dictionary < string , string > options )
{
Options opt = new Options ( options );
if ( string . IsNullOrEmpty ( opt . SignatureCachePath ))
2009-06-14 21:51:06 +00:00
throw new Exception ( Strings . Interface . SignatureCachePathMissingError );
2009-03-23 20:44:25 +00:00
else
System . IO . Directory . Delete ( opt . SignatureCachePath , true );
}
2009-03-29 21:59:00 +00:00
public static List < KeyValuePair < RSync . RSyncDir . PatchFileType , string >> ListActualSignatureFiles ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . ListActualSignatureFiles ();
}
2010-10-23 11:40:43 +00:00
public static void CreateFolder ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
i . CreateFolder ();
}
2010-10-23 13:10:54 +00:00
public static List < KeyValuePair < string , DateTime >> FindLastFileVersion ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . FindLastFileVersion ();
}
2011-03-10 18:26:38 +00:00
public static List < KeyValuePair < BackupEntryBase , Exception >> VerifyBackup ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . VerifyBackupChain ();
}
2010-08-12 18:32:15 +00:00
#endregion
2009-02-15 23:27:14 +00:00
#region IDisposable Members
public void Dispose ()
{
2011-03-10 19:08:16 +00:00
if ( m_options != null && m_options . LoadedModules != null )
{
2012-08-15 18:33:46 +00:00
foreach ( KeyValuePair < bool , Library . Interface . IGenericModule > mx in m_options . LoadedModules )
if ( mx . Key && mx . Value is Duplicati . Library . Interface . IGenericCallbackModule )
try { (( Duplicati . Library . Interface . IGenericCallbackModule ) mx . Value ). OnFinish ( m_result ); }
catch ( Exception ex ) { Logging . Log . WriteMessage ( string . Format ( "OnFinish callback {0} failed: {1}" , mx . Key , ex . Message ), Duplicati . Library . Logging . LogMessageType . Warning , ex ); }
2011-03-10 19:08:16 +00:00
foreach ( KeyValuePair < bool , Library . Interface . IGenericModule > mx in m_options . LoadedModules )
if ( mx . Key && mx . Value is IDisposable )
2012-08-15 18:33:46 +00:00
try { (( IDisposable ) mx . Value ). Dispose (); }
catch ( Exception ex ) { Logging . Log . WriteMessage ( string . Format ( "Dispose for {0} failed: {1}" , mx . Key , ex . Message ), Duplicati . Library . Logging . LogMessageType . Warning , ex ); }
2011-03-10 19:08:16 +00:00
m_options . LoadedModules . Clear ();
2011-06-17 13:31:28 +00:00
OperationRunning ( false );
2011-03-10 19:08:16 +00:00
}
2012-04-11 19:41:13 +00:00
if ( m_hasSetLogging && Logging . Log . CurrentLog is Logging . StreamLog )
{
Logging . StreamLog sl = ( Logging . StreamLog ) Logging . Log . CurrentLog ;
Logging . Log . CurrentLog = null ;
sl . Dispose ();
m_hasSetLogging = false ;
}
2009-02-15 23:27:14 +00:00
}
#endregion
2010-01-03 22:46:58 +00:00
#region ILiveControl Members
public void Pause ()
{
m_liveControl . Pause ();
}
public void Resume ()
{
m_liveControl . Resume ();
}
public void Stop ()
{
m_liveControl . Stop ();
}
public void Terminate ()
{
m_liveControl . Terminate ();
}
public bool IsStopRequested
{
get { return m_liveControl . IsStopRequested ; }
}
2011-06-17 13:31:28 +00:00
public bool IsPauseRequested
{
get { return m_liveControl . IsPauseRequested ; }
}
2010-01-03 22:46:58 +00:00
public void SetUploadLimit ( string limit )
{
m_liveControl . SetUploadLimit ( limit );
}
public void SetDownloadLimit ( string limit )
{
m_liveControl . SetDownloadLimit ( limit );
}
public void SetThreadPriority ( System . Threading . ThreadPriority priority )
{
m_liveControl . SetThreadPriority ( priority );
}
public void UnsetThreadPriority ()
{
m_liveControl . UnsetThreadPriority ();
}
#endregion
2009-01-14 17:29:30 +00:00
}
}