Files
duplicati/Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs
T

207 lines
8.5 KiB
C#
Raw Normal View History

2025-01-07 09:40:39 +01:00
// Copyright (C) 2025, The Duplicati Team
2024-02-28 15:45:30 +01:00
// https://duplicati.com, hello@duplicati.com
2014-07-06 08:52:43 -04:00
//
2024-02-28 15:45:30 +01:00
// 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:
2014-07-06 08:52:43 -04:00
//
2024-02-28 15:45:30 +01:00
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
2014-07-06 08:52:43 -04:00
//
2024-02-28 15:45:30 +01:00
// 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.
2014-07-06 08:52:43 -04:00
using Duplicati.Library.Interface;
2024-12-20 16:10:08 +01:00
using Microsoft.Azure.Storage.Shared.Protocol;
2014-07-06 08:52:43 -04:00
using System.Collections.Generic;
using System.IO;
2019-04-28 11:50:28 -07:00
using System.Threading;
using System.Threading.Tasks;
2014-07-06 08:52:43 -04:00
namespace Duplicati.Library.Backend.AzureBlob
{
2019-04-28 11:50:28 -07:00
// ReSharper disable once UnusedMember.Global
// This class is instantiated dynamically in the BackendLoader.
2014-07-06 08:52:43 -04:00
public class AzureBlobBackend : IStreamingBackend
{
private readonly AzureBlobWrapper _azureBlob;
2019-04-28 11:51:31 -07:00
// ReSharper disable once UnusedMember.Global
// This constructor is needed by the BackendLoader.
2014-07-06 08:52:43 -04:00
public AzureBlobBackend()
{
}
2019-04-28 11:51:31 -07:00
// ReSharper disable once UnusedMember.Global
// This constructor is needed by the BackendLoader.
2014-07-06 08:52:43 -04:00
public AzureBlobBackend(string url, Dictionary<string, string> options)
{
var uri = new Utility.Uri(url);
uri.RequireHost();
string storageAccountName = null;
string accessKey = null;
string sasToken = null;
string containerName = uri.Host.ToLowerInvariant();
if (options.ContainsKey("auth-username"))
storageAccountName = options["auth-username"];
if (options.ContainsKey("auth-password"))
accessKey = options["auth-password"];
if (options.ContainsKey("azure-account-name"))
2021-05-15 21:03:26 +10:00
storageAccountName = options["azure-account-name"];
if (options.ContainsKey("azure-access-key"))
2021-05-15 21:03:26 +10:00
accessKey = options["azure-access-key"];
if (options.ContainsKey("azure-access-sas-token"))
sasToken = options["azure-access-sas-token"];
if (!string.IsNullOrEmpty(uri.Username))
storageAccountName = uri.Username;
if (!string.IsNullOrEmpty(uri.Password))
accessKey = uri.Password;
if (string.IsNullOrWhiteSpace(storageAccountName))
2014-07-06 08:52:43 -04:00
{
throw new UserInformationException(Strings.AzureBlobBackend.NoStorageAccountName, "AzureNoAccountName");
2014-07-06 08:52:43 -04:00
}
if (string.IsNullOrWhiteSpace(accessKey) && string.IsNullOrWhiteSpace(sasToken))
2014-07-06 08:52:43 -04:00
{
throw new UserInformationException(Strings.AzureBlobBackend.NoAccessKeyOrSasToken, "AzureNoAccessKeyOrSasToken");
2014-07-06 08:52:43 -04:00
}
_azureBlob = new AzureBlobWrapper(storageAccountName, accessKey, sasToken, containerName);
2014-07-06 08:52:43 -04:00
}
public string DisplayName
{
get { return Strings.AzureBlobBackend.DisplayName; }
}
public string ProtocolKey
{
get { return "azure"; }
}
2025-02-17 16:45:51 +01:00
public IAsyncEnumerable<IFileEntry> ListAsync(CancellationToken cancelToken)
=> _azureBlob.ListContainerEntriesAsync(cancelToken);
2014-07-06 08:52:43 -04:00
2021-06-12 11:30:08 -07:00
public async Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
2014-07-06 08:52:43 -04:00
{
using (var fs = File.Open(localname,
FileMode.Open, FileAccess.Read, FileShare.Read))
{
2021-06-12 11:30:08 -07:00
await PutAsync(remotename, fs, cancelToken);
2014-07-06 08:52:43 -04:00
}
}
2024-12-20 16:10:08 +01:00
public Task PutAsync(string remotename, Stream input, CancellationToken cancelToken)
2014-07-06 08:52:43 -04:00
{
2024-12-20 16:10:08 +01:00
return WrapWithExceptionHandler(_azureBlob.AddFileStream(remotename, input, cancelToken));
2014-07-06 08:52:43 -04:00
}
public async Task GetAsync(string remotename, string localname, CancellationToken cancellationToken)
2014-07-06 08:52:43 -04:00
{
using (var fs = File.Open(localname,
FileMode.Create, FileAccess.Write,
FileShare.None))
{
await GetAsync(remotename, fs, cancellationToken).ConfigureAwait(false);
2014-07-06 08:52:43 -04:00
}
}
public Task GetAsync(string remotename, Stream output, CancellationToken cancellationToken)
2014-07-06 08:52:43 -04:00
{
2024-12-20 16:10:08 +01:00
return WrapWithExceptionHandler(_azureBlob.GetFileStreamAsync(remotename, output, cancellationToken));
2014-07-06 08:52:43 -04:00
}
2024-09-29 23:04:54 +02:00
public Task DeleteAsync(string remotename, CancellationToken cancellationToken)
2014-07-06 08:52:43 -04:00
{
2024-12-20 16:10:08 +01:00
return WrapWithExceptionHandler(_azureBlob.DeleteObjectAsync(remotename, cancellationToken));
2014-07-06 08:52:43 -04:00
}
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
2021-02-15 15:53:13 +00:00
new CommandLineArgument("azure-account-name",
CommandLineArgument.ArgumentType.String,
Strings.AzureBlobBackend.StorageAccountNameDescriptionShort,
Strings.AzureBlobBackend.StorageAccountNameDescriptionLong),
new CommandLineArgument("azure-access-key",
CommandLineArgument.ArgumentType.Password,
Strings.AzureBlobBackend.AccessKeyDescriptionShort,
Strings.AzureBlobBackend.AccessKeyDescriptionLong),
new CommandLineArgument("azure-access-sas-token",
CommandLineArgument.ArgumentType.Password,
Strings.AzureBlobBackend.SasTokenDescriptionShort,
Strings.AzureBlobBackend.SasTokenDescriptionLong),
2021-02-15 15:53:13 +00:00
new CommandLineArgument("auth-password",
CommandLineArgument.ArgumentType.Password,
Strings.AzureBlobBackend.AuthPasswordDescriptionShort,
Strings.AzureBlobBackend.AuthPasswordDescriptionLong),
2021-02-15 15:53:13 +00:00
new CommandLineArgument("auth-username",
CommandLineArgument.ArgumentType.String,
Strings.AzureBlobBackend.AuthUsernameDescriptionShort,
Strings.AzureBlobBackend.AuthUsernameDescriptionLong)
2014-07-06 08:52:43 -04:00
});
}
}
public string Description
{
get
{
return Strings.AzureBlobBackend.Description_v2;
}
2019-04-28 11:50:28 -07:00
}
2024-10-02 09:37:52 +02:00
public Task<string[]> GetDNSNamesAsync(CancellationToken cancelToken) => Task.FromResult(_azureBlob.DnsNames);
2019-04-28 11:50:28 -07:00
public Task TestAsync(CancellationToken cancellationToken)
2025-02-17 16:45:51 +01:00
=> this.TestListAsync(cancellationToken);
2014-07-06 08:52:43 -04:00
public Task CreateFolderAsync(CancellationToken cancellationToken)
2014-07-06 08:52:43 -04:00
{
2024-12-20 16:10:08 +01:00
return WrapWithExceptionHandler(_azureBlob.AddContainerAsync(cancellationToken));
}
private async Task WrapWithExceptionHandler(Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (Microsoft.WindowsAzure.Storage.StorageException e)
when (e.RequestInformation.HttpStatusCode == 404
|| e.RequestInformation.HttpStatusMessage == StorageErrorCodeStrings.ResourceNotFound)
{
throw new FileMissingException(e.Message, e);
}
catch (Microsoft.WindowsAzure.Storage.StorageException e)
when (e.RequestInformation.HttpStatusMessage == StorageErrorCodeStrings.ContainerNotFound
|| e.RequestInformation.HttpStatusMessage == StorageErrorCodeStrings.ContainerBeingDeleted
|| e.RequestInformation.HttpStatusMessage == StorageErrorCodeStrings.ContainerDisabled)
{
throw new FolderMissingException(e.Message, e);
}
2014-07-06 08:52:43 -04:00
}
public void Dispose()
{
}
}
}