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-05-08 21:29:59 +02:00
using System ;
using Duplicati.Library.Main.Database ;
using System.Collections.Generic ;
2020-04-05 11:41:48 -07:00
using System.Data ;
2013-05-08 21:29:59 +02:00
using System.Linq ;
2019-09-29 20:16:28 -07:00
using Duplicati.Library.Interface ;
2013-05-08 21:29:59 +02:00
2013-05-25 16:40:15 +02:00
namespace Duplicati.Library.Main.Operation
2013-05-08 21:29:59 +02:00
{
2013-08-20 22:16:30 +02:00
internal static class FilelistProcessor
2013-05-08 21:29:59 +02:00
{
2018-03-12 14:07:11 +01:00
/// <summary>
/// The tag used for logging
/// </summary>
private static readonly string LOGTAG = Logging . Log . LogTagFromType ( typeof ( FilelistProcessor ));
2014-12-30 17:29:29 +01:00
/// <summary>
/// Helper method that verifies uploaded volumes and updates their state in the database.
/// Throws an error if there are issues with the remote storage
/// </summary>
/// <param name="database">The database to compare with</param>
2018-10-06 13:30:13 -07:00
public static void VerifyLocalList ( BackendManager backend , LocalDatabase database )
2014-12-30 17:29:29 +01:00
{
var locallist = database . GetRemoteVolumes ();
foreach ( var i in locallist )
{
switch ( i . State )
{
case RemoteVolumeState . Uploaded :
case RemoteVolumeState . Verified :
case RemoteVolumeState . Deleted :
break ;
case RemoteVolumeState . Temporary :
case RemoteVolumeState . Deleting :
case RemoteVolumeState . Uploading :
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "RemovingStaleFile" , "Removing remote file listed as {0}: {1}" , i . State , i . Name );
2014-12-30 17:29:29 +01:00
try
{
backend . Delete ( i . Name , i . Size , true );
}
catch ( Exception ex )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "DeleteFileFailed" , ex , "Failed to erase file {0}, treating as deleted: {1}" , i . Name , ex . Message );
2014-12-30 17:29:29 +01:00
}
break ;
default :
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "UnknownFileState" , null , "Unknown state for remote file listed as {0}: {1}" , i . State , i . Name );
2014-12-30 17:29:29 +01:00
break ;
}
backend . FlushDbMessages ();
}
}
2020-04-05 12:00:51 -07:00
public static void VerifyRemoteList ( BackendManager backend , Options options , LocalDatabase database , IBackendWriter backendWriter , bool latestVolumesOnly , IDbTransaction transaction )
2020-04-05 11:41:48 -07:00
{
if (! options . NoBackendverification )
{
LocalBackupDatabase backupDatabase = new LocalBackupDatabase ( database , options );
IEnumerable < string > protectedFiles = backupDatabase . GetTemporaryFilelistVolumeNames ( latestVolumesOnly , transaction );
2020-04-05 12:00:51 -07:00
FilelistProcessor . VerifyRemoteList ( backend , options , database , backendWriter , protectedFiles );
2020-04-05 11:41:48 -07:00
}
}
2013-05-08 21:29:59 +02:00
/// <summary>
/// Helper method that verifies uploaded volumes and updates their state in the database.
/// Throws an error if there are issues with the remote storage
/// </summary>
/// <param name="backend">The backend instance to use</param>
/// <param name="options">The options used</param>
/// <param name="database">The database to compare with</param>
2014-12-30 17:29:29 +01:00
/// <param name="log">The log instance to use</param>
2020-02-29 16:33:32 -06:00
/// <param name="protectedFiles">Filenames that should be exempted from deletion</param>
public static void VerifyRemoteList ( BackendManager backend , Options options , LocalDatabase database , IBackendWriter log , IEnumerable < string > protectedFiles = null )
2016-09-15 11:39:27 +02:00
{
2020-02-29 16:33:32 -06:00
var tp = RemoteListAnalysis ( backend , options , database , log , protectedFiles );
2016-09-15 11:39:27 +02:00
long extraCount = 0 ;
long missingCount = 0 ;
2020-02-29 16:33:32 -06:00
2016-09-15 11:39:27 +02:00
foreach ( var n in tp . ExtraVolumes )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "ExtraUnknownFile" , null , "Extra unknown file: {0}" , n . File . Name );
2016-09-15 11:39:27 +02:00
extraCount ++;
}
2013-05-08 21:29:59 +02:00
2016-09-15 11:39:27 +02:00
foreach ( var n in tp . MissingVolumes )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "MissingFile" , null , "Missing file: {0}" , n . Name );
2016-09-15 11:39:27 +02:00
missingCount ++;
}
2013-05-08 21:29:59 +02:00
2016-09-15 11:39:27 +02:00
if ( extraCount > 0 )
{
var s = string . Format ( "Found {0} remote files that are not recorded in local storage, please run repair" , extraCount );
2018-03-12 14:07:11 +01:00
Logging . Log . WriteErrorMessage ( LOGTAG , "ExtraRemoteFiles" , null , s );
2021-05-18 21:28:23 -07:00
throw new RemoteListVerificationException ( s , "ExtraRemoteFiles" );
2016-09-15 11:39:27 +02:00
}
2013-05-08 21:29:59 +02:00
2017-09-28 21:55:15 -07:00
ISet < string > doubles ;
Library . Utility . Utility . GetUniqueItems ( tp . ParsedVolumes . Select ( x => x . File . Name ), out doubles );
2016-01-06 21:52:30 +01:00
if ( doubles . Count > 0 )
{
2017-09-22 22:05:38 -07:00
var s = string . Format ( "Found remote files reported as duplicates, either the backend module is broken or you need to manually remove the extra copies.\nThe following files were found multiple times: {0}" , string . Join ( ", " , doubles ));
2018-03-12 14:07:11 +01:00
Logging . Log . WriteErrorMessage ( LOGTAG , "DuplicateRemoteFiles" , null , s );
2021-05-18 21:28:23 -07:00
throw new RemoteListVerificationException ( s , "DuplicateRemoteFiles" );
2016-01-06 21:52:30 +01:00
}
2013-05-08 21:29:59 +02:00
if ( missingCount > 0 )
{
2016-09-15 11:39:27 +02:00
string s ;
2013-05-13 22:32:05 +02:00
if (! tp . BackupPrefixes . Contains ( options . Prefix ) && tp . BackupPrefixes . Length > 0 )
2016-09-15 11:39:27 +02:00
s = string . Format ( "Found {0} files that are missing from the remote storage, and no files with the backup prefix {1}, but found the following backup prefixes: {2}" , missingCount , options . Prefix , string . Join ( ", " , tp . BackupPrefixes ));
2013-05-08 21:29:59 +02:00
else
2016-09-15 11:39:27 +02:00
s = string . Format ( "Found {0} files that are missing from the remote storage, please run repair" , missingCount );
2020-02-29 16:33:32 -06:00
2018-03-12 14:07:11 +01:00
Logging . Log . WriteErrorMessage ( LOGTAG , "MissingRemoteFiles" , null , s );
2021-05-18 21:28:23 -07:00
throw new RemoteListVerificationException ( s , "MissingRemoteFiles" );
2020-02-29 16:33:32 -06:00
}
2013-05-08 21:29:59 +02:00
}
public struct RemoteAnalysisResult
{
public IEnumerable < Volumes . IParsedVolume > ParsedVolumes ;
public IEnumerable < Volumes . IParsedVolume > ExtraVolumes ;
2015-01-07 09:38:42 +01:00
public IEnumerable < Volumes . IParsedVolume > OtherVolumes ;
2013-05-08 21:29:59 +02:00
public IEnumerable < RemoteVolumeEntry > MissingVolumes ;
2014-08-26 12:51:09 +02:00
public IEnumerable < RemoteVolumeEntry > VerificationRequiredVolumes ;
2015-01-07 09:38:42 +01:00
public string [] BackupPrefixes { get { return ParsedVolumes . Union ( ExtraVolumes ). Union ( OtherVolumes ). Select ( x => x . Prefix ). Distinct (). ToArray (); } }
2013-05-08 21:29:59 +02:00
}
2013-07-01 14:40:45 +02:00
/// <summary>
/// Creates a temporary verification file.
/// </summary>
/// <returns>The verification file.</returns>
/// <param name="db">The database instance</param>
/// <param name="stream">The stream to write to</param>
public static void CreateVerificationFile ( LocalDatabase db , System . IO . StreamWriter stream )
{
var s = new Newtonsoft . Json . JsonSerializer ();
2016-08-28 18:55:14 +02:00
s . Serialize ( stream , db . GetRemoteVolumes (). Where ( x => x . State != RemoteVolumeState . Temporary ). Cast < IRemoteVolume >(). ToArray ());
2013-07-01 14:40:45 +02:00
}
2020-02-29 16:33:32 -06:00
2013-07-01 14:40:45 +02:00
/// <summary>
/// Uploads the verification file.
/// </summary>
/// <param name="backendurl">The backend url</param>
/// <param name="options">The options to use</param>
/// <param name="result">The result writer</param>
/// <param name="db">The attached database</param>
/// <param name="transaction">An optional transaction object</param>
public static void UploadVerificationFile ( string backendurl , Options options , IBackendWriter result , LocalDatabase db , System . Data . IDbTransaction transaction )
{
using ( var backend = new BackendManager ( backendurl , options , result , db ))
using ( var tempfile = new Library . Utility . TempFile ())
{
var remotename = options . Prefix + "-verification.json" ;
using ( var stream = new System . IO . StreamWriter ( tempfile , false , System . Text . Encoding . UTF8 ))
FilelistProcessor . CreateVerificationFile ( db , stream );
2020-02-29 16:33:32 -06:00
2013-07-01 14:40:45 +02:00
if ( options . Dryrun )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteDryrunMessage ( LOGTAG , "WouldUploadVerificationFile" , "Would upload verification file: {0}, size: {1}" , remotename , Library . Utility . Utility . FormatSizeString ( new System . IO . FileInfo ( tempfile ). Length ));
2013-07-01 14:40:45 +02:00
}
else
{
backend . PutUnencrypted ( remotename , tempfile );
backend . WaitForComplete ( db , transaction );
}
}
}
2013-05-08 21:29:59 +02:00
/// <summary>
/// Helper method that verifies uploaded volumes and updates their state in the database.
/// Throws an error if there are issues with the remote storage
/// </summary>
/// <param name="backend">The backend instance to use</param>
/// <param name="options">The options used</param>
/// <param name="database">The database to compare with</param>
2020-02-29 16:33:32 -06:00
/// <param name="protectedFiles">Filenames that should be exempted from deletion</param>
public static RemoteAnalysisResult RemoteListAnalysis ( BackendManager backend , Options options , LocalDatabase database , IBackendWriter log , IEnumerable < string > protectedFiles )
2013-05-08 21:29:59 +02:00
{
var rawlist = backend . List ();
var lookup = new Dictionary < string , Volumes . IParsedVolume >();
2020-02-29 16:33:32 -06:00
protectedFiles = protectedFiles ?? Enumerable . Empty < string >();
2013-05-08 21:29:59 +02:00
2014-08-19 20:20:01 +02:00
var remotelist = ( from n in rawlist
let p = Volumes . VolumeBase . ParseFilename ( n )
2015-01-07 09:38:42 +01:00
where p != null && p . Prefix == options . Prefix
2014-08-19 20:20:01 +02:00
select p ). ToList ();
2015-01-07 09:38:42 +01:00
var otherlist = ( from n in rawlist
let p = Volumes . VolumeBase . ParseFilename ( n )
where p != null && p . Prefix != options . Prefix
select p ). ToList ();
2014-08-19 20:20:01 +02:00
var unknownlist = ( from n in rawlist
let p = Volumes . VolumeBase . ParseFilename ( n )
where p == null
select n ). ToList ();
2015-01-07 09:38:42 +01:00
2014-08-19 20:20:01 +02:00
var filesets = ( from n in remotelist
where n . FileType == RemoteVolumeType . Files orderby n . Time descending
select n ). ToList ();
2015-01-07 09:38:42 +01:00
2017-09-22 20:52:19 -07:00
log . KnownFileCount = remotelist . Count ;
2018-02-07 11:23:52 -07:00
long knownFileSize = remotelist . Select ( x => Math . Max ( 0 , x . File . Size )). Sum ();
log . KnownFileSize = knownFileSize ;
2017-09-22 20:52:19 -07:00
log . UnknownFileCount = unknownlist . Count ;
2016-04-06 22:10:36 +02:00
log . UnknownFileSize = unknownlist . Select ( x => Math . Max ( 0 , x . Size )). Sum ();
2019-08-18 14:42:39 -04:00
log . BackupListCount = database . FilesetTimes . Count ();
2013-05-25 16:40:15 +02:00
log . LastBackupDate = filesets . Count == 0 ? new DateTime ( 0 ) : filesets [ 0 ]. Time . ToLocalTime ();
2016-09-23 13:46:42 +02:00
2017-09-24 17:30:52 -06:00
// TODO: We should query through the backendmanager
using ( var bk = DynamicLoader . BackendLoader . GetBackend ( backend . BackendUrl , options . RawOptions ))
2019-09-29 20:16:28 -07:00
if ( bk is IQuotaEnabledBackend enabledBackend )
2017-09-24 17:30:52 -06:00
{
2019-09-29 20:16:28 -07:00
Library . Interface . IQuotaInfo quota = enabledBackend . Quota ;
2017-09-24 17:30:52 -06:00
if ( quota != null )
{
log . TotalQuotaSpace = quota . TotalQuotaSpace ;
log . FreeQuotaSpace = quota . FreeQuotaSpace ;
2018-02-07 11:23:52 -07:00
// Check to see if there should be a warning or error about the quota
// Since this processor may be called multiple times during a backup
// (both at the start and end, for example), the log keeps track of
// whether a quota error or warning has been sent already.
// Note that an error can still be sent later even if a warning was sent earlier.
if (! log . ReportedQuotaError && quota . FreeQuotaSpace == 0 )
{
log . ReportedQuotaError = true ;
2018-03-12 14:07:11 +01:00
Logging . Log . WriteErrorMessage ( LOGTAG , "BackendQuotaExceeded" , null , "Backend quota has been exceeded: Using {0} of {1} ({2} available)" , Library . Utility . Utility . FormatSizeString ( knownFileSize ), Library . Utility . Utility . FormatSizeString ( quota . TotalQuotaSpace ), Library . Utility . Utility . FormatSizeString ( quota . FreeQuotaSpace ));
2018-02-07 11:23:52 -07:00
}
else if (! log . ReportedQuotaWarning && ! log . ReportedQuotaError && quota . FreeQuotaSpace >= 0 ) // Negative value means the backend didn't return the quota info
{
// Warnings are sent if the available free space is less than the given percentage of the total backup size.
double warningThreshold = options . QuotaWarningThreshold / ( double ) 100 ;
if ( quota . FreeQuotaSpace < warningThreshold * knownFileSize )
{
log . ReportedQuotaWarning = true ;
2020-02-29 16:33:32 -06:00
Logging . Log . WriteWarningMessage ( LOGTAG , "BackendQuotaNear" , null , "Backend quota is close to being exceeded: Using {0} of {1} ({2} available)" , Library . Utility . Utility . FormatSizeString ( knownFileSize ), Library . Utility . Utility . FormatSizeString ( quota . TotalQuotaSpace ), Library . Utility . Utility . FormatSizeString ( quota . FreeQuotaSpace ));
2018-02-07 11:23:52 -07:00
}
}
2017-09-24 17:30:52 -06:00
}
}
2013-05-25 16:40:15 +02:00
log . AssignedQuotaSpace = options . QuotaSize ;
2020-02-29 16:33:32 -06:00
2014-08-19 20:20:01 +02:00
foreach ( var s in remotelist )
2015-01-07 09:38:42 +01:00
lookup [ s . File . Name ] = s ;
2020-02-29 16:33:32 -06:00
2013-05-08 21:29:59 +02:00
var missing = new List < RemoteVolumeEntry >();
2014-08-26 12:51:09 +02:00
var missingHash = new List < Tuple < long , RemoteVolumeEntry >>();
2016-03-16 00:49:28 +01:00
var cleanupRemovedRemoteVolumes = new HashSet < string >();
2016-03-24 16:31:07 +01:00
foreach ( var e in database . DuplicateRemoteVolumes ())
{
2016-03-30 01:30:51 +02:00
if ( e . Value == RemoteVolumeState . Uploading || e . Value == RemoteVolumeState . Temporary )
database . UnlinkRemoteVolume ( e . Key , e . Value );
2016-03-24 16:31:07 +01:00
else
2021-05-18 21:28:23 -07:00
throw new RemoteListVerificationException ( string . Format ( "The remote volume {0} appears in the database with state {1} and a deleted state, cannot continue" , e . Key , e . Value . ToString ()), "AmbiguousStateRemoteFiles" );
2016-03-24 16:31:07 +01:00
}
2013-05-08 21:29:59 +02:00
var locallist = database . GetRemoteVolumes ();
2014-08-19 20:20:01 +02:00
foreach ( var i in locallist )
2013-05-08 21:29:59 +02:00
{
2014-08-19 20:20:01 +02:00
Volumes . IParsedVolume r ;
var remoteFound = lookup . TryGetValue ( i . Name , out r );
var correctSize = remoteFound && i . Size >= 0 && ( i . Size == r . File . Size || r . File . Size < 0 );
lookup . Remove ( i . Name );
switch ( i . State )
2013-05-08 21:29:59 +02:00
{
2014-08-19 20:20:01 +02:00
case RemoteVolumeState . Deleted :
if ( remoteFound )
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "IgnoreRemoteDeletedFile" , "ignoring remote file listed as {0}: {1}" , i . State , i . Name );
2014-08-19 20:20:01 +02:00
break ;
case RemoteVolumeState . Temporary :
case RemoteVolumeState . Deleting :
if ( remoteFound )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "RemoveUnwantedRemoteFile" , "removing remote file listed as {0}: {1}" , i . State , i . Name );
2014-08-19 20:20:01 +02:00
backend . Delete ( i . Name , i . Size , true );
}
else
2013-05-08 21:29:59 +02:00
{
2016-02-22 21:27:12 +01:00
if ( i . DeleteGracePeriod > DateTime . UtcNow )
2015-04-05 14:33:13 +02:00
{
2018-04-11 23:02:47 +02:00
Logging . Log . WriteInformationMessage ( LOGTAG , "KeepDeleteRequest" , "keeping delete request for {0} until {1}" , i . Name , i . DeleteGracePeriod . ToLocalTime ());
2015-04-05 14:33:13 +02:00
}
else
{
2020-02-29 16:33:32 -06:00
if ( i . State == RemoteVolumeState . Temporary && protectedFiles . Any ( pf => pf == i . Name ))
2016-12-24 11:42:26 +01:00
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "KeepIncompleteFile" , "keeping protected incomplete remote file listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
}
else
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "RemoteUnwantedMissingFile" , "removing file listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
cleanupRemovedRemoteVolumes . Add ( i . Name );
}
2015-04-05 14:33:13 +02:00
}
2013-05-08 21:29:59 +02:00
}
2014-08-19 20:20:01 +02:00
break ;
case RemoteVolumeState . Uploading :
if ( remoteFound && correctSize && r . File . Size >= 0 )
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "PromotingCompleteFile" , "promoting uploaded complete file from {0} to {2}: {1}" , i . State , i . Name , RemoteVolumeState . Uploaded );
2014-08-19 20:20:01 +02:00
database . UpdateRemoteVolume ( i . Name , RemoteVolumeState . Uploaded , i . Size , i . Hash );
}
2014-12-30 18:26:54 +01:00
else if (! remoteFound )
{
2020-02-29 16:33:32 -06:00
if ( protectedFiles . Any ( pf => pf == i . Name ))
2016-12-24 11:42:26 +01:00
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "KeepIncompleteFile" , "keeping protected incomplete remote file listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
database . UpdateRemoteVolume ( i . Name , RemoteVolumeState . Temporary , i . Size , i . Hash , false , new TimeSpan ( 0 ), null );
}
else
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "SchedulingMissingFileForDelete" , "scheduling missing file for deletion, currently listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
cleanupRemovedRemoteVolumes . Add ( i . Name );
database . UpdateRemoteVolume ( i . Name , RemoteVolumeState . Deleting , i . Size , i . Hash , false , TimeSpan . FromHours ( 2 ), null );
}
2014-12-30 18:26:54 +01:00
}
2014-08-19 20:20:01 +02:00
else
{
2020-02-29 16:33:32 -06:00
if ( protectedFiles . Any ( pf => pf == i . Name ))
2016-12-24 11:42:26 +01:00
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "KeepIncompleteFile" , "keeping protected incomplete remote file listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
}
else
{
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "Remove incomplete file" , "removing incomplete remote file listed as {0}: {1}" , i . State , i . Name );
2016-12-24 11:42:26 +01:00
backend . Delete ( i . Name , i . Size , true );
}
2014-08-19 20:20:01 +02:00
}
break ;
2014-08-26 12:51:09 +02:00
case RemoteVolumeState . Uploaded :
2014-08-19 20:20:01 +02:00
if (! remoteFound )
missing . Add ( i );
else if ( correctSize )
database . UpdateRemoteVolume ( i . Name , RemoteVolumeState . Verified , i . Size , i . Hash );
2013-05-08 21:29:59 +02:00
else
2014-08-26 12:51:09 +02:00
missingHash . Add ( new Tuple < long , RemoteVolumeEntry >( r . File . Size , i ));
2014-08-19 20:20:01 +02:00
break ;
case RemoteVolumeState . Verified :
if (! remoteFound )
2013-05-08 21:29:59 +02:00
missing . Add ( i );
2014-08-19 20:20:01 +02:00
else if (! correctSize )
2014-08-26 12:51:09 +02:00
missingHash . Add ( new Tuple < long , RemoteVolumeEntry >( r . File . Size , i ));
2013-05-08 21:29:59 +02:00
2014-08-19 20:20:01 +02:00
break ;
2020-02-29 16:33:32 -06:00
2014-08-19 20:20:01 +02:00
default :
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "UnknownFileState" , null , "unknown state for remote file listed as {0}: {1}" , i . State , i . Name );
2014-08-19 20:20:01 +02:00
break ;
2013-05-08 21:29:59 +02:00
}
2014-08-19 20:20:01 +02:00
2014-12-30 16:15:21 +01:00
backend . FlushDbMessages ();
2013-05-08 21:29:59 +02:00
}
2014-08-26 12:51:09 +02:00
2016-03-16 00:49:28 +01:00
// cleanup deleted volumes in DB en block
database . RemoveRemoteVolumes ( cleanupRemovedRemoteVolumes , null );
2014-12-30 16:15:21 +01:00
2014-08-26 12:51:09 +02:00
foreach ( var i in missingHash )
2018-03-12 14:07:11 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "MissingRemoteHash" , null , "remote file {1} is listed as {0} with size {2} but should be {3}, please verify the sha256 hash \"{4}\"" , i . Item2 . State , i . Item2 . Name , i . Item1 , i . Item2 . Size , i . Item2 . Hash );
2020-02-29 16:33:32 -06:00
return new RemoteAnalysisResult ()
{
ParsedVolumes = remotelist ,
2015-01-07 09:38:42 +01:00
OtherVolumes = otherlist ,
2020-02-29 16:33:32 -06:00
ExtraVolumes = lookup . Values ,
MissingVolumes = missing ,
VerificationRequiredVolumes = missingHash . Select ( x => x . Item2 )
2015-01-07 09:38:42 +01:00
};
2013-05-08 21:29:59 +02:00
}
2020-02-29 16:33:32 -06:00
}
2013-05-08 21:29:59 +02:00
}