2009-01-14 17:29:30 +00:00
#region Disclaimer / License
2010-01-03 22:57:13 +00:00
// Copyright (C) 2010, 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 ;
namespace Duplicati.Library.Main
{
2009-02-15 23:27:14 +00:00
public enum DuplicatiOperation
2009-01-14 17:29:30 +00:00
{
2009-02-15 23:27:14 +00:00
Backup ,
Restore ,
List ,
Remove
};
2009-03-26 19:39:50 +00:00
public delegate void OperationProgressEvent ( Interface caller , DuplicatiOperation operation , int progress , int subprogress , string message , string submessage );
2009-02-15 23:27:14 +00:00
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
2009-02-15 23:27:14 +00:00
private string m_backend ;
2009-03-22 22:55:04 +00:00
private Options m_options ;
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>
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-01-16 16:18:59 +00:00
private string m_lastProgressMessage = "" ;
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 ;
2010-01-03 22:46:58 +00:00
/// <summary>
/// The live control interface
/// </summary>
private LiveControl . LiveControl m_liveControl ;
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>
private void Interface_OperationProgress ( Interface caller , DuplicatiOperation operation , int progress , int subprogress , string message , string submessage )
{
m_lastProgressMessage = message ;
2009-03-26 19:39:50 +00:00
}
2010-01-03 22:46:58 +00:00
private void CheckLiveControl ()
2009-03-26 19:39:50 +00:00
{
2010-01-03 22:46:58 +00:00
m_liveControl . PauseIfRequested ();
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
{
2009-01-17 15:27:51 +00:00
BackupStatistics bs = new BackupStatistics ();
2010-08-12 18:32:15 +00:00
SetupCommonOptions ( bs );
2009-03-26 19:39:50 +00:00
BackendWrapper backend = null ;
2009-03-30 20:17:54 +00:00
2010-05-12 08:41:54 +00:00
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . ManifestsMustBeReadOnBackups );
2009-01-14 17:29:30 +00:00
2010-05-12 08:41:54 +00:00
if ( sources == null || sources . Length == 0 )
throw new Exception ( Strings . Interface . NoSourceFoldersError );
2010-05-24 09:00:03 +00:00
//Make sure they all have the same format
for ( int i = 0 ; i < sources . Length ; i ++)
sources [ i ] = Core . Utility . AppendDirSeperator ( sources [ i ]);
2010-05-12 08:41:54 +00:00
//Sanity check for duplicate folders and multiple inclusions of the same folder
for ( int i = 0 ; i < sources . Length - 1 ; i ++)
2010-08-12 18:56:04 +00:00
{
if (! System . IO . Directory . Exists ( sources [ i ]))
throw new System . IO . IOException ( String . Format ( Strings . Interface . SourceFolderIsMissingError , sources [ i ]));
2010-05-12 08:41:54 +00:00
for ( int j = i + 1 ; j < sources . Length ; j ++)
if ( sources [ i ]. Equals ( sources [ j ], Core . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
throw new Exception ( string . Format ( Strings . Interface . SourceDirIsIncludedMultipleTimesError , sources [ i ]));
else if ( sources [ i ]. StartsWith ( sources [ j ], Core . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
throw new Exception ( string . Format ( Strings . Interface . SourceDirsAreRelatedError , sources [ i ], sources [ j ]));
2010-08-12 18:56:04 +00:00
}
2010-05-12 08:41:54 +00:00
2010-08-25 20:59:47 +00:00
if ( m_options . AsynchronousUpload )
{
m_asyncReserved = ASYNC_RESERVED ;
m_allowUploadProgress = false ;
}
2010-05-12 08:41:54 +00:00
using ( new Logging . Timer ( "Backup from " + string . Join ( ";" , sources ) + " to " + m_backend ))
2009-01-14 17:29:30 +00:00
{
2009-01-25 19:33:36 +00:00
try
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . Backup , - 1 , - 1 , Strings . Interface . StatusLoadingFilelist , "" );
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , - 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 ;
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-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , ( 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
2010-05-12 08:41:54 +00:00
List < ManifestEntry > backupsets = backend . GetBackupSets ();
2009-03-26 19:39:50 +00:00
if ( backupsets . Count == 0 )
2009-01-25 19:33:36 +00:00
full = true ;
2009-03-30 20:17:54 +00:00
2009-03-22 22:55:04 +00:00
if (! full )
2009-03-26 19:39:50 +00:00
full = DateTime . Now > m_options . FullIfOlderThan ( backupsets [ backupsets . Count - 1 ]. Time );
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 ();
2009-02-10 23:29:01 +00:00
using ( Core . TempFolder tempfolder = new Duplicati . Library . Core . TempFolder ())
2009-01-14 17:29:30 +00:00
{
2010-06-19 21:08:21 +00:00
List < Library . Interface . ICompression > patches = new List < Duplicati . Library . Interface . ICompression >();
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 ();
2010-09-04 12:23:03 +00:00
patches = FindPatches ( backend , entries , tempfolder , true , 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-05-12 08:41:54 +00:00
Manifestfile latest = GetManifest ( backend , backupsets [ 0 ]);
//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 );
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 )
if ( s1 . Equals ( s2 , Core . Utility . IsFSCaseSensitive ? StringComparison . CurrentCulture : StringComparison . CurrentCultureIgnoreCase ))
{
found = true ;
break ;
}
if (! found )
{
if ( m_options . FullIfSourceFolderChanged )
{
Logging . Log . WriteMessage ( "Source folders changed, issuing full backup" , Duplicati . Library . Logging . LogMessageType . Information );
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 ;
}
}
2009-01-14 17:29:30 +00:00
}
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 ;
}
2009-03-11 20:56:01 +00:00
2009-01-25 19:33:36 +00:00
DateTime backuptime = DateTime . Now ;
2009-01-14 17:29:30 +00:00
2010-09-12 21:50:09 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , - 1 , - 1 , Strings . Interface . StatusBuildingFilelist , "" );
2010-05-12 08:41:54 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( manifest . SourceDirs , bs , 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-08-12 18:32:15 +00:00
dir . InitiateMultiPassDiff ( full , m_options . SnapShotStrategy );
2009-01-14 17:29:30 +00:00
2010-08-20 21:28:28 +00:00
string tempVolumeFolder = m_options . AsynchronousUpload ? m_options . AsynchronousUploadFolder : ( m_options . TempDir ?? Core . TempFolder . SystemTempPath );
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-08-17 16:09:36 +00:00
using ( Core . TempFile signaturefile = new Duplicati . Library . Core . TempFile ( System . IO . Path . Combine ( tempVolumeFolder , Guid . NewGuid (). ToString ())))
using ( Core . TempFile contentfile = new Duplicati . Library . Core . TempFile ( System . IO . Path . Combine ( tempVolumeFolder , Guid . NewGuid (). ToString ())))
2009-01-14 17:29:30 +00:00
{
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , ( 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
2010-06-19 21:08:21 +00:00
using ( Library . Interface . ICompression signaturearchive = DynamicLoader . CompressionLoader . GetModule ( m_options . CompressionModule , signaturefile , m_options . RawOptions ))
using ( Library . Interface . ICompression contentarchive = DynamicLoader . CompressionLoader . 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 ))
2009-02-15 23:27:14 +00:00
using ( System . IO . Stream cs = signaturearchive . CreateFile ( System . IO . Path . Combine ( RSync . RSyncDir . CONTROL_ROOT , System . IO . Path . GetFileName ( s ))))
2009-02-14 17:30:52 +00:00
using ( System . IO . FileStream fs = System . IO . File . OpenRead ( s ))
Core . Utility . CopyStream ( fs , cs );
//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
2010-08-25 20:59:47 +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 , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingContentVolume , vol + 1 ), "" );
2010-01-03 22:46:58 +00:00
//Last check before we upload, we do not interrupt transfers
CheckLiveControl ();
2009-01-20 17:17:46 +00:00
2010-05-12 08:41:54 +00:00
//The backendwrapper will remove these
signaturefile . Protected = true ;
contentfile . Protected = true ;
manifest . ContentHashes . Add ( Core . Utility . CalculateHash ( contentfile ));
2009-02-10 23:29:01 +00:00
using ( new Logging . Timer ( "Writing delta file " + ( vol + 1 ). ToString ()))
2010-05-12 08:41:54 +00:00
backend . Put ( new ContentEntry ( backuptime , full , vol + 1 ), contentfile );
2009-01-14 17:29:30 +00:00
2010-08-25 20:59:47 +00:00
if (! m_options . AsynchronousUpload )
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingSignatureVolume , vol + 1 ), "" );
2010-01-03 22:46:58 +00:00
2010-05-12 08:41:54 +00:00
manifest . SignatureHashes . Add ( Core . Utility . CalculateHash ( signaturefile ));
2009-02-10 23:29:01 +00:00
using ( new Logging . Timer ( "Writing remote signatures" ))
2010-05-12 08:41:54 +00:00
backend . Put ( new SignatureEntry ( backuptime , full , vol + 1 ), signaturefile );
2009-01-15 10:46:57 +00:00
}
2009-02-10 23:29:01 +00:00
2010-05-12 08:41:54 +00:00
//The backend wrapper will remove these
2009-02-10 23:29:01 +00:00
Core . TempFile mf = new Duplicati . Library . Core . TempFile ();
mf . Protected = true ;
using ( new Logging . Timer ( "Writing manifest" ))
{
2010-05-12 08:41:54 +00:00
manifest . Save ( mf );
2009-02-10 23:29:01 +00:00
2010-08-25 20:59:47 +00:00
if (! m_options . AsynchronousUpload )
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusUploadingManifestVolume , vol + 1 ), "" );
2009-03-11 20:56:01 +00:00
2010-05-12 08:41:54 +00:00
//Alternate primary/secondary
backend . Put ( new ManifestEntry ( backuptime , full , manifest . SignatureHashes . Count % 2 != 0 ), mf );
2009-02-10 23:29:01 +00:00
}
2010-08-25 20:59:47 +00:00
if ( m_options . AsynchronousUpload )
m_allowUploadProgress = false ;
2009-03-30 20:17:54 +00:00
//The file volume counter
2009-02-10 23:29:01 +00:00
vol ++;
2009-01-14 17:29:30 +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 ();
}
else
throw new InvalidOperationException ();
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), - 1 , msg , "" );
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 )
{
2010-08-25 20:59:47 +00:00
//If this is a controlled user-requested stop, wait for the current upload to complete
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 )
2009-03-30 20:17:54 +00:00
throw ; //This also activates "finally", unlike in other languages...
2010-09-11 12:43:55 +00:00
bs . LogError ( string . Format ( Strings . Interface . PartialUploadMessage , backend . ManifestUploads , ex . Message ), ex );
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 ;
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 )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . Backup , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-03-26 19:39:50 +00:00
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , 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 ;
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 )
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), progress , m_lastProgressMessage , filename );
2010-01-16 16:18:59 +00:00
}
/// <summary>
/// Event handler for reporting backup/restore progress
/// </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-01-03 22:46:58 +00:00
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusProcessing , filename ), "" );
2010-01-03 22:46:58 +00:00
CheckLiveControl ();
2009-03-11 20:56:01 +00:00
}
2010-05-12 08:41:54 +00:00
/// <summary>
/// Will attempt to read the manifest file, optinally revering to the secondary manifest if reading one fails.
/// </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 ;
}
bool parsingError = false ;
using ( new Logging . Timer ( "Get " + entry . Filename ))
using ( Core . TempFile tf = new Duplicati . Library . Core . TempFile ())
{
try
{
backend . Get ( entry , tf , null );
//We now have the file decrypted, if the next step fails,
// its a broken xml or invalid content
parsingError = true ;
Manifestfile mf = new Manifestfile ( tf );
if ( m_options . SkipFileHashChecks )
{
mf . SignatureHashes = null ;
mf . ContentHashes = null ;
}
return mf ;
}
catch ( Exception ex )
{
//Only try secondary if the parsing/decrypting fails, not if the transfer fails
if ( entry . Alternate != null && ( ex is System . Security . Cryptography . CryptographicException || parsingError ))
{
//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
{
2009-01-17 15:27:51 +00:00
RestoreStatistics rs = new RestoreStatistics ();
2010-08-12 18:32:15 +00:00
SetupCommonOptions ( rs );
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 ;
2009-01-25 19:33:36 +00:00
2009-02-15 23:27:14 +00:00
using ( new Logging . Timer ( "Restore from " + m_backend + " to " + target ))
2009-01-14 17:29:30 +00:00
{
2009-01-25 19:33:36 +00:00
try
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . Restore , - 1 , - 1 , Strings . Interface . StatusStarted , "" );
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , - 1 , - 1 , Strings . Interface . StatusStarted , "" );
2009-02-15 23:27:14 +00:00
2009-03-22 22:55:04 +00:00
Core . 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
{
List < Core . IFilenameFilter > list = new List < Duplicati . Library . Core . IFilenameFilter >();
2009-03-22 22:55:04 +00:00
list . Add ( new Core . FilelistFilter ( true , m_options . FileToRestore . Split ( System . IO . Path . PathSeparator )));
2009-02-12 21:52:58 +00:00
list . Add ( new Core . RegularExpressionFilter ( false , ".*" ));
filter = new Duplicati . Library . Core . FilenameFilter ( list );
}
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 );
OperationProgress ( this , DuplicatiOperation . Restore , ( 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
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 ;
2009-01-14 20:13:59 +00:00
2010-01-16 16:18:59 +00:00
int patchCount = 0 ;
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
patchCount += be . Volumes . Count ;
2010-01-16 16:18:59 +00:00
2009-02-12 21:52:58 +00:00
using ( RSync . RSyncDir sync = new Duplicati . Library . Main . RSync . RSyncDir ( target , rs , filter ))
2009-02-10 23:29:01 +00:00
{
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2009-03-09 21:09:27 +00:00
{
2010-01-16 16:18:59 +00:00
m_progress = (( 1.0 - INCREMENAL_COST ) * ( patchno / ( double ) patchCount )) + INCREMENAL_COST ;
2010-05-12 08:41:54 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusReadingManifest , be . Filename ), "" );
CheckLiveControl ();
2009-03-09 21:09:27 +00:00
2010-05-12 08:41:54 +00:00
Manifestfile manifest = 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-01-16 16:18:59 +00:00
m_progress = (( 1.0 - INCREMENAL_COST ) * ( patchno / ( double ) patchCount )) + INCREMENAL_COST ;
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
2009-02-10 23:29:01 +00:00
using ( Core . TempFile patchzip = new Duplicati . Library . Core . TempFile ())
2009-01-25 19:33:36 +00:00
{
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , ( 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 ;
using ( Core . TempFile sigFile = new Duplicati . Library . Core . TempFile ())
{
2010-01-16 16:31:25 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , ( 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 ))
backend . Get ( signatureVol , sigFile , manifest . SignatureHashes == null ? null : manifest . SignatureHashes [ signatureVol . Volumenumber - 1 ]);
}
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 )
using ( Library . Interface . ICompression patch = DynamicLoader . CompressionLoader . GetModule ( signatureVol . Compression , sigFile , m_options . RawOptions ))
{
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 )
continue ; //Avoid downloading the content file
2010-01-16 16:18:59 +00:00
}
2010-01-16 16:31:25 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , ( 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 ))
backend . Get ( contentVol , patchzip , manifest . ContentHashes == null ? null : manifest . ContentHashes [ contentVol . Volumenumber - 1 ]);
2009-03-09 21:09:27 +00:00
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusPatching , patchno + 1 ), "" );
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 )))
using ( Library . Interface . ICompression patch = DynamicLoader . CompressionLoader . 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
}
2009-01-25 19:33:36 +00:00
finally
{
if ( backend != null )
backend . Dispose ();
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . Restore , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Restore , 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 ;
//TODO: The RS should have the number of restored files, and the size of those
2009-01-20 17:17:46 +00:00
//but that is a little difficult, because some may be restored, and then removed
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
{
RestoreStatistics rs = new RestoreStatistics ();
2010-08-12 18:32:15 +00:00
SetupCommonOptions ( rs );
2009-02-14 17:30:52 +00:00
2009-03-26 19:39:50 +00:00
BackendWrapper backend = null ;
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 )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . Restore , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-02-14 17:30:52 +00:00
2009-03-26 19:39:50 +00:00
backend = new BackendWrapper ( rs , m_backend , m_options );
2009-02-14 17:30:52 +00:00
2010-05-12 08:41:54 +00:00
List < ManifestEntry > attempts = backend . GetBackupSets ();
2009-02-14 17:30:52 +00:00
2010-05-12 08:41:54 +00:00
List < ManifestEntry > flatlist = new List < ManifestEntry >();
foreach ( ManifestEntry be in attempts )
2009-02-14 17:30:52 +00:00
{
flatlist . Add ( be );
flatlist . AddRange ( be . Incrementals );
}
flatlist . Reverse ();
2009-02-15 23:27:14 +00:00
string prefix = Core . Utility . AppendDirSeperator ( RSync . RSyncDir . CONTROL_ROOT );
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in flatlist )
2009-02-14 17:30:52 +00:00
{
2010-05-12 08:41:54 +00:00
if ( be . Volumes . Count > 0 )
2009-02-14 17:30:52 +00:00
using ( Core . TempFile z = new Duplicati . Library . Core . TempFile ())
{
2010-05-12 08:41:54 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , 0 , - 1 , string . Format ( Strings . Interface . StatusReadingIncrementalFile , be . Volumes [ 0 ]. Key . Filename ), "" );
2009-02-15 23:27:14 +00:00
2010-05-12 08:41:54 +00:00
Manifestfile mf = GetManifest ( backend , be );
using ( new Logging . Timer ( "Get " + be . Volumes [ 0 ]. Key . Filename ))
backend . Get ( be . Volumes [ 0 ]. Key , z , mf . SignatureHashes == null ? null : mf . SignatureHashes [ 0 ]);
2009-02-14 17:30:52 +00:00
2010-06-19 21:08:21 +00:00
using ( Library . Interface . ICompression fz = DynamicLoader . CompressionLoader . GetModule ( be . Volumes [ 0 ]. Key . Compression , z , m_options . RawOptions ))
2009-02-14 17:30:52 +00:00
{
bool any = false ;
2009-02-15 23:27:14 +00:00
foreach ( string f in fz . ListFiles ( prefix ))
2009-02-14 17:30:52 +00:00
{
any = true ;
using ( System . IO . Stream s1 = fz . OpenRead ( f ))
2009-02-15 23:27:14 +00:00
using ( System . IO . Stream s2 = System . IO . File . Create ( System . IO . Path . Combine ( target , f . Substring ( prefix . Length ))))
2009-02-14 17:30:52 +00:00
Core . Utility . CopyStream ( s1 , s2 );
}
if ( any )
break ;
2010-09-11 12:43:55 +00:00
rs . LogError ( string . Format ( Strings . Interface . FailedToFindControlFilesMessage , be . Volumes [ 0 ]. Key . Filename ), null );
2009-02-14 17:30:52 +00:00
}
}
}
}
finally
{
if ( backend != null )
backend . Dispose ();
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . Restore , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-02-14 17:30:52 +00:00
}
}
rs . EndTime = DateTime . Now ;
return rs . ToString ();
}
2010-06-19 21:08:21 +00:00
public string DeleteAllButNFull ()
2009-01-17 15:27:51 +00:00
{
2010-01-18 22:22:42 +00:00
int x = Math . Max ( 0 , m_options . RemoveAllButNFull );
2009-01-17 15:27:51 +00:00
2010-01-18 22:22:42 +00:00
StringBuilder sb = new StringBuilder ();
using ( BackendWrapper backend = new BackendWrapper ( new CommunicationStatistics (), m_backend , m_options ))
2009-01-25 19:33:36 +00:00
try
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . Remove , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-01-20 17:17:46 +00:00
2010-05-12 08:41:54 +00:00
List < ManifestEntry > entries = backend . GetBackupSets ();
List < ManifestEntry > toremove = new List < ManifestEntry >();
2009-01-17 15:27:51 +00:00
2009-01-25 19:33:36 +00:00
while ( entries . Count > x )
{
2010-01-18 22:22:42 +00:00
if ( entries . Count == 1 && ! m_options . AllowFullRemoval )
{
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingLastFullMessage , entries [ 0 ]. Time ));
break ;
}
2010-05-12 08:41:54 +00:00
ManifestEntry be = entries [ 0 ];
2009-01-25 19:33:36 +00:00
entries . RemoveAt ( 0 );
2009-01-17 15:27:51 +00:00
2009-01-25 19:33:36 +00:00
be . Incrementals . Reverse ();
toremove . AddRange ( be . Incrementals );
toremove . Add ( be );
}
2009-01-17 15:27:51 +00:00
2010-01-18 22:22:42 +00:00
if ( entries . Count == 0 && toremove . Count > 0 && ! m_options . AllowFullRemoval )
throw new Exception ( Strings . Interface . InternalDeleteCountError );
sb . Append ( RemoveBackupSets ( backend , toremove ));
2009-01-25 19:33:36 +00:00
}
finally
{
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . Remove , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-01-25 19:33:36 +00:00
}
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 ();
2009-03-22 22:55:04 +00:00
DateTime expires = m_options . RemoveOlderThan ;
2009-01-17 15:27:51 +00:00
2009-03-26 19:39:50 +00:00
using ( BackendWrapper backend = new BackendWrapper ( new CommunicationStatistics (), m_backend , m_options ))
2009-01-25 19:33:36 +00:00
try
{
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . Remove , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-01-17 15:27:51 +00:00
2010-05-12 08:41:54 +00:00
List < ManifestEntry > entries = backend . GetBackupSets ();
List < ManifestEntry > toremove = new List < ManifestEntry >();
2009-01-17 15:27:51 +00:00
2009-06-11 17:23:41 +00:00
while ( entries . Count > 0 && entries [ 0 ]. Time <= expires )
2009-01-25 19:33:36 +00:00
{
2010-01-18 22:22:42 +00:00
if ( entries . Count == 1 && ! m_options . AllowFullRemoval )
{
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingLastFullMessage , entries [ 0 ]. Time ));
break ;
}
2010-05-12 08:41:54 +00:00
ManifestEntry be = entries [ 0 ];
2009-01-25 19:33:36 +00:00
entries . RemoveAt ( 0 );
bool hasNewer = false ;
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry bex in be . Incrementals )
2009-06-11 17:23:41 +00:00
if ( bex . Time >= expires )
2009-01-25 19:33:36 +00:00
{
hasNewer = true ;
break ;
}
2009-01-17 15:27:51 +00:00
2009-01-25 19:33:36 +00:00
if ( hasNewer )
2009-01-17 15:27:51 +00:00
{
2010-05-12 08:41:54 +00:00
List < ManifestEntry > t = new List < ManifestEntry >( be . Incrementals );
2009-01-25 19:33:36 +00:00
t . Insert ( 0 , be );
for ( int i = 0 ; i < t . Count ; i ++)
2009-06-11 17:23:41 +00:00
if ( t [ i ]. Time <= expires )
2009-06-14 21:51:06 +00:00
sb . AppendLine ( string . Format ( Strings . Interface . NotDeletingBackupSetMessage , t [ i ]. Time . ToString ( System . Globalization . CultureInfo . InvariantCulture )));
2009-01-25 19:33:36 +00:00
2009-01-17 15:27:51 +00:00
break ;
}
2009-01-25 19:33:36 +00:00
else
{
be . Incrementals . Reverse ();
toremove . AddRange ( be . Incrementals );
toremove . Add ( be );
}
}
2009-01-17 15:27:51 +00:00
2010-01-18 22:22:42 +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
2009-02-15 23:27:14 +00:00
sb . Append ( RemoveBackupSets ( backend , toremove ));
2009-01-25 19:33:36 +00:00
}
finally
{
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . Remove , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
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 ();
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry me in entries )
2009-01-17 15:27:51 +00:00
{
2010-05-12 08:41:54 +00:00
sb . AppendLine ( string . Format ( Strings . Interface . DeletingBackupSetMessage , me . Time . ToString ( System . Globalization . CultureInfo . InvariantCulture )));
2009-01-17 15:27:51 +00:00
2009-03-22 22:55:04 +00:00
if ( m_options . Force )
2009-01-17 15:27:51 +00:00
{
//Delete manifest
2010-08-19 16:53:05 +00:00
if ( me . Alternate != null )
backend . Delete ( me . Alternate );
2010-05-12 08:41:54 +00:00
backend . Delete ( me );
2009-01-17 15:27:51 +00:00
2010-05-12 08:41:54 +00:00
foreach ( KeyValuePair < SignatureEntry , ContentEntry > kx in me . Volumes )
{
backend . Delete ( kx . Key );
backend . Delete ( kx . Value );
}
2009-01-17 15:27:51 +00:00
}
}
2009-03-22 22:55:04 +00:00
if (! m_options . Force && entries . Count > 0 )
2009-06-14 21:51:06 +00:00
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-08-12 18:32:15 +00:00
SetupCommonOptions ( null );
2009-01-14 17:29:30 +00:00
List < string > res = new List < string >();
2010-06-19 21:08:21 +00:00
Duplicati . Library . Interface . IBackend i = Duplicati . Library . DynamicLoader . BackendLoader . GetBackend ( m_backend , m_options . RawOptions );
2009-01-25 19:33:36 +00:00
if ( i == null )
2009-06-14 21:51:06 +00:00
throw new Exception ( string . Format ( Strings . Interface . BackendNotFoundError , m_backend ));
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . List , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-01-25 19:33:36 +00:00
using ( i )
2010-06-19 21:08:21 +00:00
foreach ( Duplicati . Library . Interface . IFileEntry fe in i . List ())
2009-01-25 19:33:36 +00:00
res . Add ( fe . Name );
2009-01-14 17:29:30 +00:00
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . List , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-02-15 23:27:14 +00:00
2009-01-14 17:29:30 +00:00
return res . ToArray ();
}
2009-03-21 12:46:54 +00:00
public string Cleanup ()
{
bool anyRemoved = false ;
2009-03-26 19:39:50 +00:00
using ( BackendWrapper backend = new BackendWrapper ( new CommunicationStatistics (), m_backend , m_options ))
2009-03-21 12:46:54 +00:00
{
2010-05-12 08:41:54 +00:00
List < ManifestEntry > sorted = backend . GetBackupSets ();
2009-03-21 12:46:54 +00:00
2010-05-12 08:41:54 +00:00
List < ManifestEntry > entries = new List < ManifestEntry >();
2009-03-21 12:46:54 +00:00
entries . AddRange ( sorted );
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in sorted )
2009-03-21 12:46:54 +00:00
entries . AddRange ( be . Incrementals );
2009-03-26 19:39:50 +00:00
backend . DeleteOrphans ();
2009-03-21 12:46:54 +00:00
2010-05-12 08:41:54 +00:00
if ( m_options . SkipFileHashChecks )
throw new Exception ( Strings . Interface . CannotCleanWithoutHashesError );
if ( m_options . DontReadManifests )
throw new Exception ( Strings . Interface . CannotCleanWithoutHashesError );
2009-03-21 12:46:54 +00:00
//Now compare the actual filelist with the manifest
2010-05-12 08:41:54 +00:00
foreach ( ManifestEntry be in entries )
2009-03-21 12:46:54 +00:00
{
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
int count = manifest . ContentHashes . Count ;
2009-03-21 12:46:54 +00:00
2010-05-12 08:41:54 +00:00
for ( int i = count - 1 ; i < be . Volumes . Count ; i ++)
{
anyRemoved = true ;
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . RemovingPartialFilesMessage , be . Volumes [ i ]. Key . Filename ), Duplicati . Library . Logging . LogMessageType . Information );
Logging . Log . WriteMessage ( string . Format ( Strings . Interface . RemovingPartialFilesMessage , be . Volumes [ i ]. Value . Filename ), Duplicati . Library . Logging . LogMessageType . Information );
if ( m_options . Force )
2009-03-21 12:46:54 +00:00
{
2010-05-12 08:41:54 +00:00
backend . Delete ( be . Volumes [ i ]. Key );
backend . Delete ( be . Volumes [ i ]. Value );
2009-03-21 12:46:54 +00:00
}
}
}
}
2009-03-22 22:55:04 +00:00
if (! m_options . Force && anyRemoved )
2009-06-14 21:51:06 +00:00
Logging . Log . WriteMessage ( Strings . Interface . FilesAreNotForceDeletedMessage , Duplicati . Library . Logging . LogMessageType . Information );
2009-03-21 12:46:54 +00:00
return "" ; //TODO: Write a message here?
}
2009-02-15 23:27:14 +00:00
public IList < string > ListContent ()
2009-02-09 06:47:00 +00:00
{
RestoreStatistics rs = new RestoreStatistics ();
2010-08-12 18:32:15 +00:00
SetupCommonOptions ( rs );
2009-02-09 06:47:00 +00:00
2009-03-22 22:55:04 +00:00
Core . FilenameFilter filter = m_options . Filter ;
2009-03-26 19:39:50 +00:00
DateTime timelimit = m_options . RestoreTime ;
2009-02-15 23:27:14 +00:00
if ( OperationStarted != null )
2009-06-14 21:51:06 +00:00
OperationStarted ( this , DuplicatiOperation . List , 0 , - 1 , Strings . Interface . StatusStarted , "" );
2009-02-09 06:47:00 +00:00
2009-03-21 12:46:54 +00:00
List < string > res ;
2009-03-26 19:39:50 +00:00
using ( BackendWrapper backend = new BackendWrapper ( rs , m_backend , m_options ))
2009-02-09 06:47:00 +00:00
using ( Core . TempFolder basefolder = new Duplicati . Library . Core . TempFolder ())
{
2010-05-12 08:41:54 +00:00
ManifestEntry bestFit = backend . GetBackupSet ( timelimit );
2009-03-26 19:39:50 +00:00
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 );
2009-02-09 06:47:00 +00:00
2010-09-04 12:23:03 +00:00
List < Library . Interface . ICompression > patches = FindPatches ( backend , entries , basefolder , false , rs );
2009-02-09 06:47:00 +00:00
2010-05-12 08:41:54 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( new string [] { basefolder }, rs , filter , patches ))
2009-03-21 12:46:54 +00:00
res = dir . UnmatchedFiles ();
2009-02-09 06:47:00 +00:00
}
2009-02-15 23:27:14 +00:00
if ( OperationCompleted != null )
2009-06-14 21:51:06 +00:00
OperationCompleted ( this , DuplicatiOperation . List , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
2009-02-09 06:47:00 +00:00
2009-03-21 12:46:54 +00:00
return res ;
2009-02-09 06:47:00 +00:00
}
2010-05-12 08:41:54 +00:00
public string [] ListSourceFolders ()
{
RestoreStatistics rs = new RestoreStatistics ();
2010-08-12 18:32:15 +00:00
SetupCommonOptions ( rs );
2010-05-12 08:41:54 +00:00
Core . FilenameFilter filter = m_options . Filter ;
DateTime timelimit = m_options . RestoreTime ;
if ( OperationStarted != null )
OperationStarted ( this , DuplicatiOperation . List , 0 , - 1 , Strings . Interface . StatusStarted , "" );
string [] res ;
using ( BackendWrapper backend = new BackendWrapper ( rs , m_backend , m_options ))
using ( Core . TempFile mfile = new Duplicati . Library . Core . TempFile ())
{
ManifestEntry bestFit = backend . GetBackupSet ( timelimit );
backend . Get ( bestFit , mfile , null );
res = new Manifestfile ( mfile ). SourceDirs ;
}
if ( OperationCompleted != null )
OperationCompleted ( this , DuplicatiOperation . List , 100 , - 1 , Strings . Interface . StatusCompleted , "" );
return res ;
}
2009-01-14 17:29:30 +00:00
2010-08-12 18:32:15 +00:00
private void SetupCommonOptions ( CommunicationStatistics stats )
2009-01-14 17:29:30 +00:00
{
2010-09-11 13:52:30 +00:00
if ( stats != null )
stats . VerboseErrors = m_options . DebugOutput ;
2009-03-22 22:55:04 +00:00
if (! string . IsNullOrEmpty ( m_options . TempDir ))
Core . TempFolder . SystemTempPath = m_options . TempDir ;
if (! string . IsNullOrEmpty ( m_options . ThreadPriority ))
System . Threading . Thread . CurrentThread . Priority = Core . Utility . ParsePriority ( m_options . ThreadPriority );
2010-06-19 21:08:21 +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 ));
2010-08-12 18:32:15 +00:00
ValidateOptions ( stats );
2010-06-19 21:08:21 +00:00
foreach ( KeyValuePair < bool , Library . Interface . IGenericModule > mx in m_options . LoadedModules )
if ( mx . Key )
mx . Value . Configure ( m_options . RawOptions );
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>
2010-09-04 12:23:03 +00:00
private List < Library . Interface . ICompression > FindPatches ( BackendWrapper backend , List < ManifestEntry > entries , string tempfolder , bool allowHashFail , CommunicationStatistics stat )
2009-03-21 12:46:54 +00:00
{
2010-06-19 21:08:21 +00:00
List < Library . Interface . ICompression > patches = new List < Library . Interface . ICompression >();
2009-03-21 12:46:54 +00:00
using ( new Logging . Timer ( "Reading incremental data" ))
{
2010-01-16 16:18:59 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , ( 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 ;
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
OperationProgress ( this , DuplicatiOperation . Backup , ( int )( m_progress * 100 ), - 1 , string . Format ( Strings . Interface . StatusReadingManifest , be . Time . ToShortDateString () + " " + be . Time . ToShortTimeString ()), "" );
2009-03-21 12:46:54 +00:00
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-05-12 08:41:54 +00:00
OperationProgress ( this , DuplicatiOperation . Backup , ( 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 ))
backend . Get ( bes . Key , filename , manifest . SignatureHashes == null ? null : manifest . SignatureHashes [ bes . Key . Volumenumber - 1 ]);
}
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
2010-06-19 21:08:21 +00:00
patches . Add ( DynamicLoader . CompressionLoader . GetModule ( bes . Key . Compression , filename , m_options . RawOptions ));
2009-03-21 12:46:54 +00:00
}
}
}
2009-03-26 19:39:50 +00:00
backend . DeleteOrphans ();
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
{
using ( BackendWrapper backend = new BackendWrapper ( new CommunicationStatistics (), m_backend , m_options ))
return backend . GetBackupSets ();
}
2009-03-29 21:59:00 +00:00
public List < KeyValuePair < RSync . RSyncDir . PatchFileType , string >> ListActualSignatureFiles ()
{
using ( BackendWrapper backend = new BackendWrapper ( new CommunicationStatistics (), m_backend , m_options ))
{
2010-05-12 08:41:54 +00:00
ManifestEntry bestFit = backend . GetBackupSet ( m_options . RestoreTime );
2009-03-29 21:59:00 +00:00
if ( bestFit . Incrementals . Count > 0 ) //Get the most recent incremental
bestFit = bestFit . Incrementals [ bestFit . Incrementals . Count - 1 ];
using ( Core . TempFolder folder = new Duplicati . Library . Core . TempFolder ())
{
2010-09-04 12:23:03 +00:00
List < Library . Interface . ICompression > patches = FindPatches ( backend , new List < ManifestEntry >( new ManifestEntry [] { bestFit }), folder , false , null );
2010-05-12 08:41:54 +00:00
using ( RSync . RSyncDir dir = new Duplicati . Library . Main . RSync . RSyncDir ( new string [] { folder }, new CommunicationStatistics (), null ))
2009-03-29 21:59:00 +00:00
return dir . ListPatchFiles ( patches );
}
}
}
2009-04-01 08:30:14 +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 ,
2010-08-15 14:57:01 +00:00
DynamicLoader . BackendLoader . GetSupportedCommands ( new Uri ( m_backend ). Scheme . ToLower ()),
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 )
{
if ( supportedOptions . ContainsKey ( a . Name ))
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 )
{
if ( supportedOptions . ContainsKey ( s ))
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
}
}
}
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 )
{
if (! string . IsNullOrEmpty ( value ) && Core . Utility . ParseBool ( value , true ) != Core . Utility . ParseBool ( value , false ))
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
{
Core . Sizeparser . ParseSize ( value );
}
catch
{
return string . Format ( Strings . Interface . UnsupportedSizeValue , optionname , value );
}
}
else if ( arg . Type == Duplicati . Library . Interface . CommandLineArgument . ArgumentType . Timespan )
{
try
{
Core . Timeparser . ParseTimeSpan ( value );
}
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 ();
foreach ( string s in Core . Utility . EnumerateFiles ( folder ))
{
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
}
public static IList < string > ListContent ( string target , Dictionary < string , string > options )
{
using ( Interface i = new Interface ( target , options ))
return i . ListContent ();
}
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
}
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-08-12 18:32:15 +00:00
#endregion
2009-02-15 23:27:14 +00:00
#region IDisposable Members
public void Dispose ()
{
}
#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 ; }
}
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
}
}