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.
2016-02-09 09:17:31 +01:00
using System ;
using CoCoL ;
using Duplicati.Library.Main.Operation.Common ;
using System.Threading.Tasks ;
2019-07-23 10:35:33 -04:00
using System.Threading ;
2016-02-09 09:17:31 +01:00
namespace Duplicati.Library.Main.Operation.Backup
{
/// <summary>
2016-02-25 19:15:51 +01:00
/// This class runs a process which opens a file and outputs data blocks for processing
2016-02-09 09:17:31 +01:00
/// </summary>
internal static class FileBlockProcessor
{
2018-04-11 23:02:47 +02:00
/// <summary>
/// The tag to use for log messages
/// </summary>
private static readonly string FILELOGTAG = Logging . Log . LogTagFromType ( typeof ( FileBlockProcessor )) + ".FileEntry" ;
2019-07-23 10:35:33 -04:00
public static Task Run ( Snapshots . ISnapshotService snapshot , Options options , BackupDatabase database , BackupStatsCollector stats , ITaskReader taskreader , CancellationToken token )
2016-02-09 09:17:31 +01:00
{
return AutomationExtensions . RunTask (
new
{
2016-02-24 23:48:42 +01:00
Input = Channels . AcceptedChangedFile . ForRead ,
2016-10-18 13:27:36 +02:00
StreamBlockChannel = Channels . StreamBlock . ForWrite ,
2016-02-09 09:17:31 +01:00
},
async self =>
{
2016-02-27 02:15:42 +01:00
while ( await taskreader . ProgressAsync )
2016-02-09 09:17:31 +01:00
{
var e = await self . Input . ReadAsync ();
try
{
2019-07-23 11:14:06 -04:00
if ( token . IsCancellationRequested )
{
break ;
}
2019-07-23 10:35:33 -04:00
2016-02-09 09:17:31 +01:00
var hint = options . GetCompressionHintFromFilename ( e . Path );
var oldHash = e . OldId < 0 ? null : await database . GetFileHashAsync ( e . OldId );
2016-10-18 13:27:36 +02:00
StreamProcessResult filestreamdata ;
2016-02-09 09:17:31 +01:00
2016-10-18 13:27:36 +02:00
// Process metadata and actual data in parallel
var metatask =
Task . Run ( async () =>
{
// If we have determined that metadata has not changed, just grab the ID
if (! e . MetadataChanged )
2016-02-09 09:17:31 +01:00
{
2016-10-18 13:27:36 +02:00
var res = await database . GetMetadataIDAsync ( e . MetaHashAndSize . FileHash , e . MetaHashAndSize . Blob . Length );
2018-08-11 11:48:19 +02:00
if ( res . Item1 )
2016-10-18 13:27:36 +02:00
return res . Item2 ;
2016-02-09 09:17:31 +01:00
2019-02-19 20:23:07 -08:00
Logging . Log . WriteWarningMessage ( FILELOGTAG , "UnexpectedMetadataLookup" , null , "Metadata was reported as not changed, but still requires being added?\nHash: {0}, Length: {1}, ID: {2}, Path: {3}" , e . MetaHashAndSize . FileHash , e . MetaHashAndSize . Blob . Length , res . Item2 , e . Path );
2016-10-18 13:27:36 +02:00
e . MetadataChanged = true ;
}
2016-02-09 09:17:31 +01:00
2018-04-11 23:02:47 +02:00
return ( await MetadataPreProcess . AddMetadataToOutputAsync ( e . Path , e . MetaHashAndSize , database , self . StreamBlockChannel )). Item2 ;
2016-10-18 13:27:36 +02:00
});
2016-02-09 09:17:31 +01:00
2016-10-18 13:27:36 +02:00
using ( var fs = snapshot . OpenRead ( e . Path ))
filestreamdata = await StreamBlock . ProcessStream ( self . StreamBlockChannel , e . Path , fs , false , hint );
2016-02-09 09:17:31 +01:00
2016-10-18 13:27:36 +02:00
await stats . AddOpenedFile ( filestreamdata . Streamlength );
2016-02-09 09:17:31 +01:00
2016-10-18 13:27:36 +02:00
var metadataid = await metatask ;
var filekey = filestreamdata . Streamhash ;
2018-06-19 12:23:32 +02:00
var filesize = filestreamdata . Streamlength ;
2016-02-09 09:17:31 +01:00
2016-10-18 13:27:36 +02:00
if ( oldHash != filekey )
{
if ( oldHash == null )
2018-04-11 23:02:47 +02:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "NewFile" , "New file {0}" , e . Path );
2016-10-18 13:27:36 +02:00
else
2018-04-11 23:02:47 +02:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "ChangedFile" , "File has changed {0}" , e . Path );
2016-10-18 13:27:36 +02:00
if ( e . OldId < 0 )
2016-02-09 09:17:31 +01:00
{
2016-10-18 13:27:36 +02:00
await stats . AddAddedFile ( filesize );
if ( options . Dryrun )
2018-10-15 21:14:59 -07:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "WouldAddNewFile" , "Would add new file {0}, size {1}" , e . Path , Library . Utility . Utility . FormatSizeString ( filesize ));
2016-02-09 09:17:31 +01:00
}
else
{
2016-10-18 13:27:36 +02:00
await stats . AddModifiedFile ( filesize );
if ( options . Dryrun )
2018-10-15 21:14:59 -07:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "WouldAddChangedFile" , "Would add changed file {0}, size {1}" , e . Path , Library . Utility . Utility . FormatSizeString ( filesize ));
2016-02-09 09:17:31 +01:00
}
2016-10-18 13:27:36 +02:00
2018-06-14 10:12:24 +02:00
await database . AddFileAsync ( e . PathPrefixID , e . Filename , e . LastWrite , filestreamdata . Blocksetid , metadataid );
2016-10-18 13:27:36 +02:00
}
else if ( e . MetadataChanged )
{
2018-04-11 23:02:47 +02:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "FileMetadataChanged" , "File has only metadata changes {0}" , e . Path );
2018-06-14 10:12:24 +02:00
await database . AddFileAsync ( e . PathPrefixID , e . Filename , e . LastWrite , filestreamdata . Blocksetid , metadataid );
2016-10-18 13:27:36 +02:00
}
2017-05-30 00:35:31 +02:00
else /*if (e.OldId >= 0)*/
2016-10-18 13:27:36 +02:00
{
// When we write the file to output, update the last modified time
2018-04-11 23:02:47 +02:00
Logging . Log . WriteVerboseMessage ( FILELOGTAG , "NoFileChanges" , "File has not changed {0}" , e . Path );
2018-08-14 11:42:38 +02:00
try
{
await database . AddUnmodifiedAsync ( e . OldId , e . LastWrite );
}
catch ( Exception ex )
{
Logging . Log . WriteWarningMessage ( FILELOGTAG , "FailedToAddFile" , ex , "Failed while attempting to add unmodified file to database: {0}" , e . Path );
}
2016-02-09 09:17:31 +01:00
}
}
catch ( Exception ex )
{
if ( ex . IsRetiredException ())
return ;
else
2018-04-11 23:02:47 +02:00
Logging . Log . WriteWarningMessage ( FILELOGTAG , "PathProcessingFailed" , ex , "Failed to process path: {0}" , e . Path );
2016-02-09 09:17:31 +01:00
}
}
2019-07-23 10:35:33 -04:00
});
2016-02-09 09:17:31 +01:00
}
}
}