Skip to content

Commit 5d32121

Browse files
committed
fix filesystem reads when container creation is disabled
1 parent b989312 commit 5d32121

4 files changed

Lines changed: 136 additions & 12 deletions

File tree

‎Directory.Build.props‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
<RepositoryUrl>https://github.com/managedcode/Storage</RepositoryUrl>
3030
<PackageProjectUrl>https://github.com/managedcode/Storage</PackageProjectUrl>
3131
<Product>Managed Code - Storage</Product>
32-
<Version>10.1.1</Version>
33-
<PackageVersion>10.1.1</PackageVersion>
32+
<Version>10.1.2</Version>
33+
<PackageVersion>10.1.2</PackageVersion>
3434

3535
</PropertyGroup>
3636

‎ManagedCode.Storage.Core/BaseStorage.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ protected Task<Result> EnsureContainerExist(CancellationToken cancellationToken
297297
return Result.Succeed()
298298
.AsTask();
299299

300+
if (!StorageOptions.CreateContainerIfNotExists)
301+
return Result.Succeed().AsTask();
302+
300303
return CreateContainerAsync(cancellationToken);
301304
}
302305

‎Storages/ManagedCode.Storage.FileSystem/FileSystemStorage.cs‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public override async Task<Result<Stream>> GetStreamAsync(string fileName, Cance
106106
await EnsureContainerExist(cancellationToken);
107107
cancellationToken.ThrowIfCancellationRequested();
108108

109-
var filePath = GetPathFromOptions(new DownloadOptions { FileName = fileName });
109+
var filePath = GetPathFromOptions(new DownloadOptions { FileName = fileName }, createDirectories: false);
110110
cancellationToken.ThrowIfCancellationRequested();
111111

112112
return File.Exists(filePath)
@@ -205,7 +205,7 @@ protected override async Task<Result<LocalFile>> DownloadInternalAsync(LocalFile
205205
await EnsureContainerExist(cancellationToken);
206206
cancellationToken.ThrowIfCancellationRequested();
207207

208-
var sourcePath = GetPathFromOptions(options);
208+
var sourcePath = GetPathFromOptions(options, createDirectories: false);
209209
cancellationToken.ThrowIfCancellationRequested();
210210

211211
if (!File.Exists(sourcePath))
@@ -248,7 +248,7 @@ protected override async Task<Result<bool>> DeleteInternalAsync(DeleteOptions op
248248
await EnsureContainerExist(cancellationToken);
249249
cancellationToken.ThrowIfCancellationRequested();
250250

251-
var filePath = GetPathFromOptions(options);
251+
var filePath = GetPathFromOptions(options, createDirectories: false);
252252
cancellationToken.ThrowIfCancellationRequested();
253253

254254
if (!File.Exists(filePath))
@@ -270,7 +270,7 @@ protected override async Task<Result<bool>> ExistsInternalAsync(ExistOptions opt
270270
await EnsureContainerExist(cancellationToken);
271271
cancellationToken.ThrowIfCancellationRequested();
272272

273-
var filePath = GetPathFromOptions(options);
273+
var filePath = GetPathFromOptions(options, createDirectories: false);
274274
cancellationToken.ThrowIfCancellationRequested();
275275

276276
return Result<bool>.Succeed(File.Exists(filePath));
@@ -289,7 +289,7 @@ protected override async Task<Result<BlobMetadata>> GetBlobMetadataInternalAsync
289289
await EnsureContainerExist(cancellationToken);
290290
cancellationToken.ThrowIfCancellationRequested();
291291

292-
var filePath = GetPathFromOptions(options);
292+
var filePath = GetPathFromOptions(options, createDirectories: false);
293293
cancellationToken.ThrowIfCancellationRequested();
294294

295295
var fileInfo = new FileInfo(filePath);
@@ -323,7 +323,7 @@ protected override async Task<Result> SetLegalHoldInternalAsync(bool hasLegalHol
323323
{
324324
try
325325
{
326-
var filePath = GetPathFromOptions(options);
326+
var filePath = GetPathFromOptions(options, createDirectories: false);
327327
await EnsureContainerExist(cancellationToken);
328328
cancellationToken.ThrowIfCancellationRequested();
329329

@@ -362,7 +362,7 @@ protected override async Task<Result<bool>> HasLegalHoldInternalAsync(LegalHoldO
362362
{
363363
try
364364
{
365-
var filePath = GetPathFromOptions(options);
365+
var filePath = GetPathFromOptions(options, createDirectories: false);
366366
await EnsureContainerExist(cancellationToken);
367367
cancellationToken.ThrowIfCancellationRequested();
368368

@@ -374,7 +374,7 @@ protected override async Task<Result<bool>> HasLegalHoldInternalAsync(LegalHoldO
374374
}
375375
}
376376

377-
private string GetPathFromOptions(BaseOptions options)
377+
private string GetPathFromOptions(BaseOptions options, bool createDirectories = true)
378378
{
379379
if (string.IsNullOrWhiteSpace(options.FileName))
380380
throw new ArgumentException("File name cannot be null or empty", nameof(options));
@@ -389,7 +389,10 @@ private string GetPathFromOptions(BaseOptions options)
389389
? SanitizeDirectory(combinedDirectory)
390390
: null;
391391

392-
if (sanitizedDirectory is not null)
392+
if (createDirectories && !StorageOptions.CreateContainerIfNotExists && !Directory.Exists(StorageClient))
393+
throw new DirectoryNotFoundException("The configured storage container does not exist and automatic creation is disabled.");
394+
395+
if (createDirectories && sanitizedDirectory is not null)
393396
{
394397
EnsureDirectoryExist(sanitizedDirectory);
395398
}
@@ -408,7 +411,8 @@ private string GetPathFromOptions(BaseOptions options)
408411
throw new UnauthorizedAccessException($"Access to path '{options.FileName}' is denied. Path traversal detected.");
409412
}
410413

411-
EnsureDirectoryExist(Path.GetDirectoryName(fullPath)!);
414+
if (createDirectories)
415+
EnsureDirectoryExist(Path.GetDirectoryName(fullPath)!);
412416
return fullPath;
413417
}
414418

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using ManagedCode.Storage.Core.Models;
5+
using ManagedCode.Storage.FileSystem;
6+
using ManagedCode.Storage.FileSystem.Options;
7+
using Shouldly;
8+
using Xunit;
9+
10+
namespace ManagedCode.Storage.Tests.Storages.FileSystem;
11+
12+
public sealed class FileSystemReadOnlyContainerTests
13+
{
14+
[Fact]
15+
public async Task ReadsAgainstMissingContainerDoNotCreateContainerOrNestedDirectories()
16+
{
17+
var root = GetTemporaryRoot();
18+
var storage = CreateStorage(root, createContainerIfNotExists: false);
19+
20+
try
21+
{
22+
var exists = await storage.ExistsAsync("nested/settings.json");
23+
var stream = await storage.GetStreamAsync("nested/settings.json");
24+
var metadata = await storage.GetBlobMetadataAsync("nested/settings.json");
25+
26+
exists.IsSuccess.ShouldBeTrue();
27+
exists.Value.ShouldBeFalse();
28+
stream.IsFailed.ShouldBeTrue();
29+
metadata.IsFailed.ShouldBeTrue();
30+
Directory.Exists(root).ShouldBeFalse();
31+
Directory.Exists(Path.Combine(root, "nested")).ShouldBeFalse();
32+
}
33+
finally
34+
{
35+
DeleteIfPresent(root);
36+
}
37+
}
38+
39+
[Fact]
40+
public async Task ReadsAgainstExistingContainerDoNotCreateMissingNestedDirectories()
41+
{
42+
var root = GetTemporaryRoot();
43+
Directory.CreateDirectory(root);
44+
var storage = CreateStorage(root, createContainerIfNotExists: false);
45+
46+
try
47+
{
48+
var exists = await storage.ExistsAsync("nested/settings.json");
49+
var stream = await storage.GetStreamAsync("nested/settings.json");
50+
var metadata = await storage.GetBlobMetadataAsync("nested/settings.json");
51+
52+
exists.IsSuccess.ShouldBeTrue();
53+
exists.Value.ShouldBeFalse();
54+
stream.IsFailed.ShouldBeTrue();
55+
metadata.IsFailed.ShouldBeTrue();
56+
Directory.Exists(Path.Combine(root, "nested")).ShouldBeFalse();
57+
}
58+
finally
59+
{
60+
DeleteIfPresent(root);
61+
}
62+
}
63+
64+
[Fact]
65+
public async Task WritesDoNotCreateMissingContainerWhenAutomaticCreationIsDisabled()
66+
{
67+
var root = GetTemporaryRoot();
68+
var storage = CreateStorage(root, createContainerIfNotExists: false);
69+
70+
try
71+
{
72+
var upload = await storage.UploadAsync(new byte[] { 1, 2, 3 }, new UploadOptions
73+
{
74+
FileName = "nested/settings.json"
75+
});
76+
77+
upload.IsFailed.ShouldBeTrue();
78+
Directory.Exists(root).ShouldBeFalse();
79+
}
80+
finally
81+
{
82+
DeleteIfPresent(root);
83+
}
84+
}
85+
86+
[Fact]
87+
public async Task ExplicitContainerCreationRemainsAvailableWhenAutomaticCreationIsDisabled()
88+
{
89+
var root = GetTemporaryRoot();
90+
var storage = CreateStorage(root, createContainerIfNotExists: false);
91+
92+
try
93+
{
94+
var created = await storage.CreateContainerAsync();
95+
96+
created.IsSuccess.ShouldBeTrue();
97+
Directory.Exists(root).ShouldBeTrue();
98+
}
99+
finally
100+
{
101+
DeleteIfPresent(root);
102+
}
103+
}
104+
105+
private static FileSystemStorage CreateStorage(string root, bool createContainerIfNotExists) => new(new FileSystemStorageOptions
106+
{
107+
BaseFolder = root,
108+
CreateContainerIfNotExists = createContainerIfNotExists
109+
});
110+
111+
private static string GetTemporaryRoot() => Path.Combine(Path.GetTempPath(), "managedcode-storage-readonly", Guid.NewGuid().ToString("N"));
112+
113+
private static void DeleteIfPresent(string path)
114+
{
115+
if (Directory.Exists(path)) Directory.Delete(path, recursive: true);
116+
}
117+
}

0 commit comments

Comments
 (0)