// Copyright (C) 2025, 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. #nullable enable using System; using System.Collections.Generic; using System.IO; using System.Linq; using Alphaleonis.Win32.Vss; using Duplicati.Library.Common.IO; namespace Duplicati.Library.Snapshots.Windows; /// /// Implementation of a snapshot, using AlphaVSS /// internal class AlphaVssBackup : ISnapshotProvider { /// /// The implementation that is being wrapped /// private IVssBackupComponents _components; /// /// Creates a new snapshot implementation /// /// The components to use private AlphaVssBackup(IVssBackupComponents components) { _components = components; } /// public void EnableWriterClasses(Guid[] guids) => _components.EnableWriterClasses(guids); /// public void DisableWriterClasses(Guid[] guids) => _components.DisableWriterClasses(guids); /// public void GatherWriterMetadata() => _components.GatherWriterMetadata(); /// public void FreeWriterMetadata() => _components.FreeWriterMetadata(); /// public void VerifyWriters(Guid[] guids) { foreach (var writerGUID in guids) { if (!_components.WriterMetadata.Any(o => o.WriterId.Equals(writerGUID))) { throw new Exception(string.Format("Writer with GUID {0} was not added to VSS writer set.", writerGUID.ToString())); } } } /// /// Stub for returning the snapshot info /// /// The path where the snapshot is located private sealed record SnapshotInfo(string SnapshotDeviceObject) : ISnapshotInfo; /// public ISnapshotInfo GetSnapshotProperties(Guid shadowId) => new SnapshotInfo(_components.GetSnapshotProperties(shadowId).SnapshotDeviceObject); /// public void StartSnapshotSet() => _components.StartSnapshotSet(); /// public void PrepareForBackup() => _components.PrepareForBackup(); /// public void DoSnapshotSet() => _components.DoSnapshotSet(); /// public bool IsVolumeSupported(string drive) => _components.IsVolumeSupported(drive); /// public Guid AddToSnapshotSet(string drive) => _components.AddToSnapshotSet(drive); /// public void BackupComplete() => _components.BackupComplete(); /// public void DeleteSnapshot(Guid shadowId, bool forceDelete) => _components.DeleteSnapshot(shadowId, forceDelete); /// public static ISnapshotProvider Create() => new AlphaVssBackup(GetVssBackupComponents()); /// /// Creates a new AlphaVssBackupComponents instance and adds the context /// /// The configured instance private static IVssBackupComponents GetVssBackupComponents() { //Prepare the backup IVssBackupComponents vssBackupComponents = CreateVssBackupComponents(); vssBackupComponents.InitializeForBackup(null); vssBackupComponents.SetContext(VssSnapshotContext.Backup); vssBackupComponents.SetBackupState(false, true, VssBackupType.Full, false); return vssBackupComponents; } /// /// Loads the AlphaVSS implementation for the current OS /// /// The implementation private static IVssBackupComponents CreateVssBackupComponents() { var vss = VssFactoryProvider.Default.GetVssFactory(); if (vss == null) throw new InvalidOperationException(); return vss.CreateVssBackupComponents(); } /// public IEnumerable ParseWriterMetaData(Guid[] writers) { // check if writers got enabled foreach (var writerGUID in writers) { var writer = _components.WriterMetadata.First(o => o.WriterId.Equals(writerGUID)); foreach (var component in writer.Components) { yield return new WriterMetaData { Guid = writerGUID, Name = component.ComponentName, LogicalPath = component.LogicalPath, Paths = GetPathsFromComponent(component) }; } } } /// /// Returns the paths from the writer metadata /// /// The component to get the paths from /// The list of paths private List GetPathsFromComponent(IVssWMComponent component) { var paths = new List(); foreach (var file in component.Files) { if (file.FileSpecification.Contains("*")) { if (Directory.Exists(Util.AppendDirSeparator(file.Path))) paths.Add(Util.AppendDirSeparator(file.Path)); } else { var fileWithSpec = SystemIO.IO_OS.PathCombine(file.Path, file.FileSpecification); if (File.Exists(fileWithSpec)) paths.Add(fileWithSpec); } } return paths; } /// public void Dispose() { _components.Dispose(); } }