2024-02-28 15:45:30 +01:00
// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2013-06-20 20:17:10 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2017-01-09 11:35:38 +01:00
using Duplicati.Library.Interface ;
2013-06-20 20:17:10 +02:00
namespace Duplicati.Library.Main.Operation
{
internal class ListChangesHandler
{
2018-03-12 14:07:11 +01:00
/// <summary>
/// The tag used for logging
/// </summary>
private static readonly string LOGTAG = Logging . Log . LogTagFromType ( typeof ( ListChangesHandler ));
2018-05-23 21:18:01 -07:00
private readonly string m_backendurl ;
private readonly Options m_options ;
private readonly ListChangesResults m_result ;
2013-06-20 20:17:10 +02:00
public ListChangesHandler ( string backend , Options options , ListChangesResults result )
{
m_backendurl = backend ;
m_options = options ;
m_result = result ;
}
private static Tuple < long , DateTime , T > SelectTime < T >( string value , IEnumerable < Tuple < long , DateTime , T >> list , out long index , out DateTime time , out T el )
{
long indexValue ;
Tuple < long , DateTime , T > res ;
if (! long . TryParse ( value , out indexValue ))
{
var t = Library . Utility . Timeparser . ParseTimeInterval ( value , DateTime . Now , true );
res = list . OrderBy ( x => Math . Abs (( x . Item2 - t ). Ticks )). First ();
}
else
{
res = list . OrderBy ( x => Math . Abs ( x . Item1 - indexValue )). First ();
}
index = res . Item1 ;
time = res . Item2 ;
el = res . Item3 ;
return res ;
}
2017-04-04 23:53:36 +02:00
public void Run ( string baseVersion , string compareVersion , IEnumerable < string > filterstrings = null , Library . Utility . IFilter compositefilter = null , Action < IListChangesResults , IEnumerable < Tuple < Library . Interface . ListChangesChangeType , Library . Interface . ListChangesElementType , string >>> callback = null )
2013-06-20 20:17:10 +02:00
{
2013-06-29 11:41:53 +02:00
var filter = Library . Utility . JoinedFilterExpression . Join ( new Library . Utility . FilterExpression ( filterstrings ), compositefilter );
2013-06-20 20:17:10 +02:00
var useLocalDb = ! m_options . NoLocalDb && System . IO . File . Exists ( m_options . Dbpath );
2013-09-13 13:26:38 +02:00
baseVersion = string . IsNullOrEmpty ( baseVersion ) ? "1" : baseVersion ;
2024-02-28 15:45:30 +01:00
compareVersion = string . IsNullOrEmpty ( compareVersion ) ? "0" : compareVersion ;
2018-09-26 20:39:14 -07:00
long baseVersionIndex ;
2024-02-28 15:45:30 +01:00
long compareVersionIndex ;
2018-09-26 20:39:14 -07:00
DateTime baseVersionTime ;
DateTime compareVersionTime ;
2013-06-20 20:17:10 +02:00
using ( var tmpdb = useLocalDb ? null : new Library . Utility . TempFile ())
using ( var db = new Database . LocalListChangesDatabase ( useLocalDb ? m_options . Dbpath : ( string ) tmpdb ))
using ( var backend = new BackendManager ( m_backendurl , m_options , m_result . BackendWriter , db ))
using ( var storageKeeper = db . CreateStorageHelper ())
{
m_result . SetDatabase ( db );
if ( useLocalDb )
{
var dbtimes = db . FilesetTimes . ToList ();
if ( dbtimes . Count < 2 )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( string . Format ( "Need at least two backups to show differences, database contains {0} backups" , dbtimes . Count ), "NeedTwoBackupsToStartDiff" );
2013-06-20 20:17:10 +02:00
long baseVersionId ;
long compareVersionId ;
var times = dbtimes . Zip ( Enumerable . Range ( 0 , dbtimes . Count ), ( a , b ) => new Tuple < long , DateTime , long >( b , a . Value , a . Key )). ToList ();
var bt = SelectTime ( baseVersion , times , out baseVersionIndex , out baseVersionTime , out baseVersionId );
times . Remove ( bt );
SelectTime ( compareVersion , times , out compareVersionIndex , out compareVersionTime , out compareVersionId );
storageKeeper . AddFromDb ( baseVersionId , false , filter );
storageKeeper . AddFromDb ( compareVersionId , true , filter );
}
else
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "NoLocalDatabase" , "No local database, accessing remote store" );
2013-06-20 20:17:10 +02:00
var parsedlist = ( from n in backend . List ()
let p = Volumes . VolumeBase . ParseFilename ( n )
where p != null && p . FileType == RemoteVolumeType . Files
orderby p . Time descending
select p ). ToArray ();
var numberedList = parsedlist . Zip ( Enumerable . Range ( 0 , parsedlist . Length ), ( a , b ) => new Tuple < long , DateTime , Volumes . IParsedVolume >( b , a . Time , a )). ToList ();
if ( numberedList . Count < 2 )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( string . Format ( "Need at least two backups to show differences, database contains {0} backups" , numberedList . Count ), "NeedTwoBackupsToStartDiff" );
2013-06-20 20:17:10 +02:00
Volumes . IParsedVolume baseFile ;
Volumes . IParsedVolume compareFile ;
var bt = SelectTime ( baseVersion , numberedList , out baseVersionIndex , out baseVersionTime , out baseFile );
numberedList . Remove ( bt );
SelectTime ( compareVersion , numberedList , out compareVersionIndex , out compareVersionTime , out compareFile );
Func < FilelistEntryType , Library . Interface . ListChangesElementType > conv = ( x ) => {
switch ( x )
{
case FilelistEntryType . File :
return Library . Interface . ListChangesElementType . File ;
case FilelistEntryType . Folder :
return Library . Interface . ListChangesElementType . Folder ;
case FilelistEntryType . Symlink :
return Library . Interface . ListChangesElementType . Symlink ;
default :
return ( Library . Interface . ListChangesElementType )(- 1 );
}
};
2014-05-15 12:47:16 +02:00
if ( m_result . TaskControlRendevouz () == TaskControlState . Stop )
return ;
2013-06-20 20:17:10 +02:00
using ( var tmpfile = backend . Get ( baseFile . File . Name , baseFile . File . Size , null ))
using ( var rd = new Volumes . FilesetVolumeReader ( RestoreHandler . GetCompressionModule ( baseFile . File . Name ), tmpfile , m_options ))
foreach ( var f in rd . Files )
2013-06-30 12:42:13 +02:00
if ( Library . Utility . FilterExpression . Matches ( filter , f . Path ))
2013-06-20 20:17:10 +02:00
storageKeeper . AddElement ( f . Path , f . Hash , f . Metahash , f . Size , conv ( f . Type ), false );
2014-05-15 12:47:16 +02:00
if ( m_result . TaskControlRendevouz () == TaskControlState . Stop )
return ;
2013-06-20 20:17:10 +02:00
using ( var tmpfile = backend . Get ( compareFile . File . Name , compareFile . File . Size , null ))
using ( var rd = new Volumes . FilesetVolumeReader ( RestoreHandler . GetCompressionModule ( compareFile . File . Name ), tmpfile , m_options ))
foreach ( var f in rd . Files )
2013-06-30 12:42:13 +02:00
if ( Library . Utility . FilterExpression . Matches ( filter , f . Path ))
2013-06-20 20:17:10 +02:00
storageKeeper . AddElement ( f . Path , f . Hash , f . Metahash , f . Size , conv ( f . Type ), true );
}
var changes = storageKeeper . CreateChangeCountReport ();
var sizes = storageKeeper . CreateChangeSizeReport ();
2017-04-04 23:53:36 +02:00
2018-03-12 14:07:11 +01:00
var lst = ( m_options . FullResult || callback != null ) ?
2017-04-04 23:53:36 +02:00
( from n in storageKeeper . CreateChangedFileReport ()
select n ) : null ;
2013-06-20 20:17:10 +02:00
m_result . SetResult (
baseVersionTime , baseVersionIndex , compareVersionTime , compareVersionIndex ,
changes . AddedFolders , changes . AddedSymlinks , changes . AddedFiles ,
changes . DeletedFolders , changes . DeletedSymlinks , changes . DeletedFiles ,
changes . ModifiedFolders , changes . ModifiedSymlinks , changes . ModifiedFiles ,
sizes . AddedSize , sizes . DeletedSize , sizes . PreviousSize , sizes . CurrentSize ,
2017-06-10 09:34:45 +01:00
( lst == null || callback == null ) ? null : lst . ToArray ()
2013-06-20 20:17:10 +02:00
);
2017-04-04 23:53:36 +02:00
if ( callback != null )
callback ( m_result , lst );
2013-06-20 20:17:10 +02:00
return ;
}
}
}
}