Properly make ZipToDirectory use IFileSystem

This commit is contained in:
Jordan Dominion
2025-08-01 10:57:26 -04:00
parent 2d7da21ee1
commit cd2233d85c
5 changed files with 42 additions and 22 deletions
@@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
/// <param name="path">The full path to extract to.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task ExtractToPath(string path, CancellationToken cancellationToken);
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
ValueTask ExtractToPath(string path, CancellationToken cancellationToken);
}
}
@@ -48,12 +48,11 @@ namespace Tgstation.Server.Host.Components.Engine
}
/// <inheritdoc />
public Task ExtractToPath(string path, CancellationToken cancellationToken)
public ValueTask ExtractToPath(string path, CancellationToken cancellationToken)
=> repository.CopyTo(
ioManager.ConcatPath(
path,
targetSubDirectory),
cancellationToken)
.AsTask();
cancellationToken);
}
}
@@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Components.Engine
public ValueTask DisposeAsync() => zipStream.DisposeAsync();
/// <inheritdoc />
public Task ExtractToPath(string path, CancellationToken cancellationToken)
public ValueTask ExtractToPath(string path, CancellationToken cancellationToken)
=> ioManager.ZipToDirectory(path, zipStream, cancellationToken);
}
}
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Utils;
namespace Tgstation.Server.Host.IO
@@ -335,26 +336,46 @@ namespace Tgstation.Server.Host.IO
TaskScheduler.Current);
/// <inheritdoc />
public Task ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken) => Task.Factory.StartNew(
() =>
{
path = ResolvePath(path);
ArgumentNullException.ThrowIfNull(zipFile);
public async ValueTask ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken)
{
path = ResolvePath(path);
ArgumentNullException.ThrowIfNull(zipFile);
#if NET9_0_OR_GREATER
#error Check if zip file seeking has been addressesed. See https://github.com/tgstation/tgstation-server/issues/1531
#endif
// ZipArchive does a synchronous copy on unseekable streams we want to avoid
if (!zipFile.CanSeek)
throw new ArgumentException("Stream does not support seeking!", nameof(zipFile));
// ZipArchive does a synchronous copy on unseekable streams we want to avoid
if (!zipFile.CanSeek)
throw new ArgumentException("Stream does not support seeking!", nameof(zipFile));
using var archive = new ZipArchive(zipFile, ZipArchiveMode.Read, true);
archive.ExtractToDirectory(path);
},
cancellationToken,
BlockingTaskCreationOptions,
TaskScheduler.Current);
using var archive = new ZipArchive(zipFile, ZipArchiveMode.Read, true);
// start async context
await Task.Yield();
foreach (var entry in archive.Entries)
{
var entryPath = fileSystem.Path.Combine(path, entry.FullName);
if (string.IsNullOrEmpty(entry.Name))
{
fileSystem.Directory.CreateDirectory(entryPath);
continue;
}
var directoryPath = fileSystem.Path.GetDirectoryName(entryPath);
if (directoryPath == null)
{
throw new JobException("Zip archive concatenation resulted in a null directory path!");
}
fileSystem.Directory.CreateDirectory(directoryPath);
using var entryStream = entry.Open();
using var outputStream = fileSystem.File.Create(entryPath);
await entryStream.CopyToAsync(outputStream, cancellationToken);
}
}
/// <inheritdoc />
public bool PathContainsParentAccess(string path) => path
+2 -2
View File
@@ -238,8 +238,8 @@ namespace Tgstation.Server.Host.IO
/// <param name="path">The path to unzip to.</param>
/// <param name="zipFile">The <see cref="Stream"/> of the <see cref="global::System.IO.Compression.ZipArchive"/>. Must have <see cref="Stream.CanSeek"/> set to <see langword="true"/>. Will be read completely and left open. <see cref="Stream.Position"/> will be indeterminate.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken);
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
ValueTask ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken);
/// <summary>
/// Get the <see cref="DateTimeOffset"/> of when a given <paramref name="path"/> was last modified.