tesses.backup/Tesses.Backup.Client/BackupContext.cs

158 lines
5.3 KiB
C#

/*
A simple Backup Client / Server
Copyright (C) 2023 Mike Nolan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Tesses.Backup.Models;
using Tesses.VirtualFilesystem;
namespace Tesses.Backup
{
public class BackupContext
{
IVirtualFilesystem fs;
string key;
HttpClient clt;
long device;
internal BackupContext(string key,HttpClient clt,IVirtualFilesystem fs,CreateBackupResponse resp,long device)
{
this.key=key;
this.clt =clt;
this.fs = fs;
this.BackupResponse = resp;
this.device =device;
CalculateLength(this.BackupResponse.BackupData,Special.Root);
}
public CreateBackupResponse BackupResponse {get;set;}
public List<(UnixPath Path,string Hash)> FilesToBackup {get;set;}=new List<(UnixPath Path,string Hash)>();
public long TotalBytes {get;set;}=0;
private void CalculateLength(List<BackupFileEntry> files,UnixPath path)
{
foreach(var file in files)
{
if(file.IsFile)
{
FilesToBackup.Add((path / file.Name,file.Hash));
TotalBytes += file.Length;
}else{
CalculateLength(file.SubEntries,path / file.Name);
}
}
}
private class CallbackStrm : Stream
{
Stream strm;
Action<int> read;
public CallbackStrm(Stream strm,Action<int> read)
{
this.strm = strm;
this.read=read;
}
public override bool CanRead => strm.CanRead;
public override bool CanSeek => strm.CanSeek;
public override bool CanWrite => false;
public override long Length => strm.Length;
public override long Position { get => strm.Position; set => strm.Position=value; }
public override void Flush()
{
strm.Flush();
}
public override async Task FlushAsync(CancellationToken cancellationToken)
{
await strm.FlushAsync(cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
int outCount= strm.Read(buffer,offset,count);
this?.read(outCount);
return outCount;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int outCount= await strm.ReadAsync(buffer,offset,count);
this?.read(outCount);
return outCount;
}
public override long Seek(long offset, SeekOrigin origin)
{
return strm.Seek(offset,origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
public async Task PerformBackupAsync(IProgress<BackupProgress> progress=null)
{
BackupProgress p=new BackupProgress();
var files = FilesToBackup;
p.FileCount = files.Count;
p.BackupBytesWriten = 0;
p.BackupTotalBytes = TotalBytes;
for(int i = 0;i<files.Count;i++)
{
var file = files[i];
p.FileName = file.Path;
p.Hash = file.Hash;
p.FileIndex = i;
p.FileBytesWriten = 0;
progress?.Report(p);
try{
using(var srcStrm = await fs.OpenAsync(file.Path,System.IO.FileMode.Open,System.IO.FileAccess.Read,System.IO.FileShare.Read))
{
p.FileTotalBytes = srcStrm.Length;
using(CallbackStrm strm = new CallbackStrm(srcStrm,(ctx)=>{
p.FileBytesWriten += ctx;
p.BackupBytesWriten += ctx;
progress?.Report(p);
}))
await clt.PutAsync($"/api/v1/File?key={key}&hash={p.Hash}&device={device}",new StreamContent(strm));
}}catch(Exception ex)
{
_=ex;
}
progress?.Report(p);
}
progress?.Report(p);
}
}
}