Files
duplicati/Duplicati/Library/Main/Volumes/VolumeBase.cs
T

196 lines
8.9 KiB
C#

// 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.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Duplicati.Library.Main.Volumes
{
public abstract class VolumeBase
{
public class FilesetData
{
[JsonProperty("IsFullBackup")]
public bool IsFullBackup { get; set; } = true;
public static string GetFilesetInstance(bool isFullBackup = true)
{
return JsonConvert.SerializeObject(new FilesetData
{
IsFullBackup = isFullBackup
});
}
}
protected class ManifestData
{
public const string ENCODING = "utf8";
public const long VERSION = 2;
public long Version;
public string Created;
public string Encoding;
public long Blocksize;
public string BlockHash;
public string FileHash;
public string AppVersion;
public static string GetManifestInstance(long blocksize, string blockhash, string filehash)
{
return JsonConvert.SerializeObject(new ManifestData()
{
Version = VERSION,
Encoding = ENCODING,
Blocksize = blocksize,
Created = Library.Utility.Utility.SerializeDateTime(DateTime.UtcNow),
BlockHash = blockhash,
FileHash = filehash,
AppVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()
});
}
public static void VerifyManifest(string manifest, long blocksize, string blockhash, string filehash)
{
var d = JsonConvert.DeserializeObject<ManifestData>(manifest);
if (d.Version > VERSION)
throw new InvalidManifestException("Version", d.Version.ToString(), VERSION.ToString());
if (d.Encoding != ENCODING)
throw new InvalidManifestException("Encoding", d.Encoding, ENCODING);
if (d.Blocksize != blocksize)
throw new InvalidManifestException("Blocksize", d.Blocksize.ToString(), blocksize.ToString());
if (d.BlockHash != blockhash)
throw new InvalidManifestException("BlockHash", d.BlockHash, blockhash);
if (d.FileHash != filehash)
throw new InvalidManifestException("FileHash", d.FileHash, filehash);
}
}
private class ParsedVolume : IParsedVolume
{
public RemoteVolumeType FileType { get; private set; }
public string Prefix { get; private set; }
public string Guid { get; private set; }
public DateTime Time { get; private set; }
public string CompressionModule { get; private set; }
public string EncryptionModule { get; private set; }
public Library.Interface.IFileEntry File { get; private set; }
internal static readonly IDictionary<RemoteVolumeType, string> REMOTE_TYPENAME_MAP;
internal static readonly IDictionary<string, RemoteVolumeType> REVERSE_REMOTE_TYPENAME_MAP;
private static readonly System.Text.RegularExpressions.Regex FILENAME_REGEXP;
static ParsedVolume()
{
var dict = new Dictionary<RemoteVolumeType, string>();
dict[RemoteVolumeType.Blocks] = "dblock";
dict[RemoteVolumeType.Files] = "dlist";
dict[RemoteVolumeType.Index] = "dindex";
var reversedict = new Dictionary<string, RemoteVolumeType>(System.StringComparer.OrdinalIgnoreCase);
foreach(var x in dict)
reversedict[x.Value] = x.Key;
REMOTE_TYPENAME_MAP = dict;
REVERSE_REMOTE_TYPENAME_MAP = reversedict;
FILENAME_REGEXP = new System.Text.RegularExpressions.Regex(@"(?<prefix>[^\-]+)\-(([i|b|I|B](?<guid>[0-9A-Fa-f]+))|((?<time>\d{8}T\d{6}Z))).(?<filetype>(" + string.Join(")|(", dict.Values) + @"))\.(?<compression>[^\.]+)(\.(?<encryption>.+))?");
}
private ParsedVolume() { }
public static IParsedVolume Parse(string filename, Library.Interface.IFileEntry file = null)
{
var m = FILENAME_REGEXP.Match(filename);
if (!m.Success || m.Length != filename.Length)
return null;
RemoteVolumeType t;
if (!REVERSE_REMOTE_TYPENAME_MAP.TryGetValue(m.Groups["filetype"].Value, out t))
return null;
return new ParsedVolume()
{
Prefix = m.Groups["prefix"].Value,
FileType = t,
Guid = m.Groups["guid"].Success ? m.Groups["guid"].Value : null,
Time = m.Groups["time"].Success ? Library.Utility.Utility.DeserializeDateTime(m.Groups["time"].Value).ToUniversalTime() : new DateTime(0, DateTimeKind.Utc),
CompressionModule = m.Groups["compression"].Value,
EncryptionModule = m.Groups["encryption"].Success ? m.Groups["encryption"].Value : null,
File = file
};
}
}
public static string GenerateFilename(RemoteVolumeType filetype, Options options, string guid, DateTime timestamp)
{
return GenerateFilename(filetype, options.Prefix, guid, timestamp, options.CompressionModule, options.NoEncryption ? null : options.EncryptionModule);
}
public static string GenerateFilename(RemoteVolumeType filetype, string prefix, string guid, DateTime timestamp, string compressionmodule, string encryptionmodule)
{
string volumename;
if (filetype == RemoteVolumeType.Files)
volumename = prefix + "-" + Library.Utility.Utility.SerializeDateTime(timestamp) + "." + (ParsedVolume.REMOTE_TYPENAME_MAP[filetype]) + "." + compressionmodule;
else
volumename = prefix + "-" + (filetype == RemoteVolumeType.Blocks ? "b" : "i") + guid + "." + (ParsedVolume.REMOTE_TYPENAME_MAP[filetype]) + "." + compressionmodule;
if (!string.IsNullOrEmpty(encryptionmodule))
volumename += "." + encryptionmodule;
return volumename;
}
public static IParsedVolume ParseFilename(Library.Interface.IFileEntry file)
{
return ParsedVolume.Parse(file.Name, file);
}
public static IParsedVolume ParseFilename(string filename)
{
return ParsedVolume.Parse(filename);
}
protected const string FILESET_FILENAME = "fileset";
protected const string MANIFEST_FILENAME = "manifest";
protected const string FILELIST = "filelist.json";
protected const string INDEX_VOLUME_FOLDER = "vol/";
protected const string INDEX_BLOCKLIST_FOLDER = "list/";
protected const string CONTROL_FILES_FOLDER = "extra/";
public static readonly System.Text.Encoding ENCODING = System.Text.Encoding.UTF8;
protected readonly long m_blocksize;
protected readonly string m_blockhash;
protected readonly string m_filehash;
protected readonly long m_blockhashsize;
protected VolumeBase(Options options)
{
m_blocksize = options.Blocksize;
m_blockhash = options.BlockHashAlgorithm;
m_filehash = options.FileHashAlgorithm;
m_blockhashsize = options.BlockhashSize;
}
}
}