2024-11-08 03:55:46 +01:00
using System ;
2024-11-08 05:56:48 +01:00
using System.Linq ;
2024-11-08 03:55:46 +01:00
using System.Threading.Tasks ;
using CoCoL ;
2024-11-08 05:56:48 +01:00
using Duplicati.Library.Main.Database ;
2024-11-08 03:55:46 +01:00
namespace Duplicati.Library.Main.Operation.Restore
{
internal class FileProcessor
{
2024-11-13 16:55:47 +01:00
public static Task Run ( LocalRestoreDatabase db , IChannel < BlockRequest > block_request , IChannel < byte []> block_response , RestoreResults results )
2024-11-08 03:55:46 +01:00
{
return AutomationExtensions . RunTask (
new
{
Input = Channels . filesToRestore . ForRead ,
},
async self =>
{
2024-11-08 05:56:48 +01:00
// TODO preallocate the file size to avoid fragmentation / help the operating system / filesystem. Verify this in a benchmark - I think it relies on OS and filesystem.
// using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None) { fs.SetLength(size); fs.Seek(0, SeekOrigin.Begin); }
2024-11-08 14:35:44 +01:00
try
2024-11-08 03:55:46 +01:00
{
2024-11-08 14:35:44 +01:00
while ( true )
2024-11-08 03:55:46 +01:00
{
2024-11-08 14:35:44 +01:00
var file = await self . Input . ReadAsync ();
2024-11-08 05:56:48 +01:00
2024-11-13 16:55:47 +01:00
var blocks = db . Connection
. CreateCommand ()
. ExecuteReaderEnumerable ( @ $"
SELECT Block.ID, Block.Hash, Block.Size, Block.VolumeID
FROM BlocksetEntry INNER JOIN Block
ON BlocksetEntry.BlockID = Block.ID
WHERE BlocksetEntry.BlocksetID = ""{file.BlocksetID}"""
)
. Select ( x =>
new BlockRequest ( x . GetInt64 ( 0 ), x . GetString ( 1 ), x . GetInt64 ( 2 ), x . GetInt64 ( 3 ))
)
. ToList ();
2024-11-08 07:20:18 +01:00
2024-11-08 15:24:56 +01:00
long bytes_written = 0 ;
2024-11-13 16:55:47 +01:00
if ( blocks . Count == 1 && blocks [ 0 ]. BlockSize == 0 )
2024-11-08 13:26:29 +01:00
{
2024-11-08 14:35:44 +01:00
// Create an empty file
using var fs = new System . IO . FileStream ( file . Path , System . IO . FileMode . OpenOrCreate , System . IO . FileAccess . Write , System . IO . FileShare . None );
2024-11-08 13:26:29 +01:00
}
2024-11-08 14:35:44 +01:00
else
2024-11-08 13:26:29 +01:00
{
2024-11-13 16:55:47 +01:00
if ( blocks . Any ( x => x . VolumeID < 0 ))
2024-11-08 14:35:44 +01:00
{
Console . WriteLine ( $"{file.Path} has a negative volume ID and positive size, skipping" );
continue ;
}
using var fs = new System . IO . FileStream ( file . Path , System . IO . FileMode . OpenOrCreate , System . IO . FileAccess . Write , System . IO . FileShare . None );
2024-11-13 16:55:47 +01:00
foreach ( var block in blocks )
2024-11-08 14:35:44 +01:00
{
2024-11-13 16:55:47 +01:00
await block_request . WriteAsync ( block );
2024-11-08 14:35:44 +01:00
var data = await block_response . ReadAsync ();
2024-11-13 16:55:47 +01:00
// TODO verify the hash, size, volume ID, and file hash
2024-11-08 14:35:44 +01:00
await fs . WriteAsync ( data );
2024-11-13 16:55:47 +01:00
bytes_written += data . Length ;
2024-11-08 14:35:44 +01:00
}
2024-11-08 13:26:29 +01:00
}
2024-11-08 10:43:19 +01:00
2024-11-08 15:24:56 +01:00
lock ( results )
{
results . RestoredFiles ++;
results . SizeOfRestoredFiles += bytes_written ;
}
2024-11-08 14:35:44 +01:00
}
}
catch ( RetiredException ex )
{
block_request . Retire ();
block_response . Retire ();
return ;
2024-11-08 03:55:46 +01:00
}
});
}
}
}