// 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 Duplicati.Library.Interface; using Duplicati.Library.Main.Volumes; using Duplicati.Library.Utility; using System.IO; namespace Duplicati.Library.Main.Operation.Common { /// /// This class encapsulates access to the backend and ensures at most one connection is active at a time /// internal class BackendHandler : SingleRunner { /// /// The tag used for logging /// private static readonly string LOGTAG = Logging.Log.LogTagFromType(); public const string VOLUME_HASH = "SHA256"; /// /// Data storage for an ongoing backend operation /// public class FileEntryItem { /// /// The current operation this entry represents /// public readonly BackendActionType Operation; /// /// The name of the remote file /// public string RemoteFilename; /// /// The name of the local file /// public string LocalFilename { get { return LocalTempfile; } } /// /// A reference to a temporary file that is disposed upon /// failure or completion of the item /// public TempFile LocalTempfile; /// /// True if the item has been encrypted /// public bool Encrypted; /// /// The expected hash value of the file /// public string Hash; /// /// The expected size of the file /// public long Size; /// /// A flag indicating if the file is a extra metadata file /// that has no entry in the database /// public bool TrackedInDb = true; /// /// A flag indicating if the operation is a retry run /// public bool IsRetry; public FileEntryItem(BackendActionType operation, string remotefilename) { Operation = operation; RemoteFilename = remotefilename; Size = -1; } public void SetLocalfilename(string name) { this.LocalTempfile = Library.Utility.TempFile.WrapExistingFile(name); this.LocalTempfile.Protected = true; } public void Encrypt(Options options) { if (!this.Encrypted && !options.NoEncryption) { var tempfile = new Library.Utility.TempFile(); using (var enc = DynamicLoader.EncryptionLoader.GetModule(options.EncryptionModule, options.Passphrase, options.RawOptions)) enc.Encrypt(this.LocalFilename, tempfile); this.DeleteLocalFile(); this.LocalTempfile = tempfile; this.Hash = null; this.Size = 0; this.Encrypted = true; } } public static string CalculateFileHash(string filename) { using (System.IO.FileStream fs = System.IO.File.OpenRead(filename)) using (var hasher = Duplicati.Library.Utility.HashAlgorithmHelper.Create(VOLUME_HASH)) return Convert.ToBase64String(hasher.ComputeHash(fs)); } public bool UpdateHashAndSize(Options options) { if (Hash == null || Size < 0) { Hash = CalculateFileHash(this.LocalFilename); Size = new System.IO.FileInfo(this.LocalFilename).Length; return true; } return false; } public void DeleteLocalFile() { if (this.LocalTempfile != null) { try { this.LocalTempfile.Protected = false; this.LocalTempfile.Dispose(); } catch (Exception ex) { Logging.Log.WriteWarningMessage(LOGTAG, "DeleteTemporaryFileError", ex, "Failed to dispose temporary file: {0}", this.LocalTempfile); } finally { this.LocalTempfile = null; } } } } } }