2016-02-09 09:17:31 +01:00
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System ;
using CoCoL ;
using Duplicati.Library.Main.Operation.Common ;
using System.Threading.Tasks ;
using System.Collections.Generic ;
using Duplicati.Library.Utility ;
using System.Linq ;
using Duplicati.Library.Interface ;
using System.IO ;
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" ;
2016-04-04 22:43:07 +02:00
public static Task Run ( Snapshots . ISnapshotService snapshot , Options options , BackupDatabase database , BackupStatsCollector stats , ITaskReader taskreader )
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
{
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
2018-07-06 18:04:47 +02:00
Logging . Log . WriteWarningMessage ( FILELOGTAG , "UnexpextedMetadataLookup" , 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
await database . AddFileAsync ( e . Path , e . LastWrite , filestreamdata . Blocksetid , metadataid );
}
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 );
2016-10-18 13:27:36 +02:00
await database . AddFileAsync ( e . Path , e . LastWrite , filestreamdata . Blocksetid , metadataid );
}
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
}
}
}
);
}
}
}