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

227 lines
9.7 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 System.IO;
using Newtonsoft.Json;
using Duplicati.Library.Interface;
namespace Duplicati.Library.Main.Volumes
{
public class FilesetVolumeWriter : VolumeWriterBase
{
private readonly Library.Utility.TempFile m_tempFile;
private readonly Stream m_tempStream;
private StreamWriter m_streamwriter;
private readonly JsonWriter m_writer;
private long m_filecount;
private long m_foldercount;
public override RemoteVolumeType FileType { get { return RemoteVolumeType.Files; } }
public FilesetVolumeWriter(Options options, DateTime timestamp)
: base(options, timestamp)
{
m_tempFile = new Library.Utility.TempFile();
m_tempStream = File.Open(m_tempFile, FileMode.Create, FileAccess.ReadWrite);
m_streamwriter = new StreamWriter(m_tempStream, ENCODING);
m_writer = new JsonTextWriter(m_streamwriter);
m_writer.WriteStartArray();
}
private void WriteMetaProperties(string metahash, long metasize, string metablockhash, IEnumerable<string> metablocklisthashes)
{
m_writer.WritePropertyName("metahash");
m_writer.WriteValue(metahash);
m_writer.WritePropertyName("metasize");
m_writer.WriteValue(metasize);
if (metablocklisthashes != null)
{
// Slightly awkward, but we avoid writing if there are no entries.
using (var en = metablocklisthashes.GetEnumerator())
{
if (en.MoveNext() && !string.IsNullOrEmpty(en.Current))
{
m_writer.WritePropertyName("metablocklists");
m_writer.WriteStartArray();
m_writer.WriteValue(en.Current);
while (en.MoveNext())
m_writer.WriteValue(en.Current);
m_writer.WriteEndArray();
}
}
}
else if (!string.IsNullOrWhiteSpace(metablockhash))
{
m_writer.WritePropertyName("metablockhash");
m_writer.WriteValue(metablockhash);
}
}
public void AddFile(string name, string filehash, long size, DateTime lastmodified, string metahash, long metasize, string metablockhash, string blockhash, long blocksize, IEnumerable<string> blocklisthashes, IEnumerable<string> metablocklisthashes)
{
AddFileEntry(FilelistEntryType.File, name, filehash, size, lastmodified, metahash, metasize, metablockhash, blockhash, blocksize, blocklisthashes, metablocklisthashes);
}
public void AddAlternateStream(string name, string filehash, long size, DateTime lastmodified, string metahash, string metablockhash, long metasize, string blockhash, long blocksize, IEnumerable<string> blocklisthashes, IEnumerable<string> metablocklisthashes)
{
AddFileEntry(FilelistEntryType.AlternateStream, name, filehash, size, lastmodified, metahash, metasize, metablockhash, blockhash, blocksize, blocklisthashes, metablocklisthashes);
}
private void AddFileEntry(FilelistEntryType type, string name, string filehash, long size, DateTime lastmodified, string metahash, long metasize, string metablockhash, string blockhash, long blocksize, IEnumerable<string> blocklisthashes, IEnumerable<string> metablocklisthashes)
{
m_filecount++;
m_writer.WriteStartObject();
m_writer.WritePropertyName("type");
m_writer.WriteValue(type.ToString());
m_writer.WritePropertyName("path");
m_writer.WriteValue(name);
m_writer.WritePropertyName("hash");
m_writer.WriteValue(filehash);
m_writer.WritePropertyName("size");
m_writer.WriteValue(size);
m_writer.WritePropertyName("time");
m_writer.WriteValue(Library.Utility.Utility.SerializeDateTime(lastmodified));
if (metahash != null)
WriteMetaProperties(metahash, metasize, metablockhash, metablocklisthashes);
if (blocklisthashes != null)
{
//Slightly awkward, but we avoid writing if there are no entries
using (var en = blocklisthashes.GetEnumerator())
{
if (en.MoveNext() && !string.IsNullOrEmpty(en.Current))
{
m_writer.WritePropertyName("blocklists");
m_writer.WriteStartArray();
m_writer.WriteValue(en.Current);
while (en.MoveNext())
m_writer.WriteValue(en.Current);
m_writer.WriteEndArray();
}
}
}
else if (!string.IsNullOrWhiteSpace(blockhash))
{
m_writer.WritePropertyName("blockhash");
m_writer.WriteValue(blockhash);
m_writer.WritePropertyName("blocksize");
m_writer.WriteValue(blocksize);
}
m_writer.WriteEndObject();
}
public void AddDirectory(string name, string metahash, long metasize, string metablockhash, IEnumerable<string> metablocklisthashes)
{
AddMetaEntry(FilelistEntryType.Folder, name, metahash, metasize, metablockhash, metablocklisthashes);
}
public void AddMetaEntry(FilelistEntryType type, string name, string metahash, long metasize, string metablockhash, IEnumerable<string> metablocklisthashes)
{
m_foldercount++;
m_writer.WriteStartObject();
m_writer.WritePropertyName("type");
m_writer.WriteValue(type.ToString());
m_writer.WritePropertyName("path");
m_writer.WriteValue(name);
if (metahash != null)
WriteMetaProperties(metahash, metasize, metablockhash, metablocklisthashes);
m_writer.WriteEndObject();
}
public override void Close()
{
if (m_streamwriter != null)
{
this.AddFilelistFile();
}
if (m_tempStream != null)
{
m_tempStream.Dispose();
}
if (m_tempFile != null)
{
m_tempFile.Dispose();
}
base.Close();
}
private void AddFilelistFile()
{
m_writer.WriteEndArray();
m_writer.Flush();
m_streamwriter.Flush();
try
{
using (Stream sr = m_compression.CreateFile(FILELIST, CompressionHint.Compressible, DateTime.UtcNow))
{
m_tempStream.Seek(0, SeekOrigin.Begin);
m_tempStream.CopyTo(sr);
sr.Flush();
}
}
finally
{
m_writer.Close();
m_streamwriter.Dispose();
m_streamwriter = null;
}
}
public void AddControlFile(string localfile, CompressionHint hint, string filename = null)
{
filename = filename ?? System.IO.Path.GetFileName(localfile);
using (var t = m_compression.CreateFile(CONTROL_FILES_FOLDER + filename, hint, DateTime.UtcNow))
using (var s = System.IO.File.OpenRead(localfile))
Library.Utility.Utility.CopyStream(s, t);
}
public override void Dispose()
{
this.Close();
base.Dispose();
}
public long FileCount { get { return m_filecount; } }
public long FolderCount { get { return m_foldercount; } }
public void AddSymlink(string name, string metahash, long metasize, string metablockhash, IEnumerable<string> metablocklisthashes)
{
AddMetaEntry(FilelistEntryType.Symlink, name, metahash, metasize, metablockhash, metablocklisthashes);
}
public void CreateFilesetFile(bool isFullBackup)
{
using (var sr = new StreamWriter(this.m_compression.CreateFile(FILESET_FILENAME, CompressionHint.Compressible, DateTime.UtcNow), ENCODING))
{
sr.Write(FilesetData.GetFilesetInstance(isFullBackup));
}
}
}
}