diff --git a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallationData.cs b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallationData.cs
index b3d8458973..0194c10d52 100644
--- a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallationData.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallationData.cs
@@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Components.Engine
///
/// The full path to extract to.
/// The for the operation.
- /// A representing the running operation.
- Task ExtractToPath(string path, CancellationToken cancellationToken);
+ /// A representing the running operation.
+ ValueTask ExtractToPath(string path, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Engine/RepositoryEngineInstallationData.cs b/src/Tgstation.Server.Host/Components/Engine/RepositoryEngineInstallationData.cs
index 88fbb77911..8b89075d4e 100644
--- a/src/Tgstation.Server.Host/Components/Engine/RepositoryEngineInstallationData.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/RepositoryEngineInstallationData.cs
@@ -48,12 +48,11 @@ namespace Tgstation.Server.Host.Components.Engine
}
///
- public Task ExtractToPath(string path, CancellationToken cancellationToken)
+ public ValueTask ExtractToPath(string path, CancellationToken cancellationToken)
=> repository.CopyTo(
ioManager.ConcatPath(
path,
targetSubDirectory),
- cancellationToken)
- .AsTask();
+ cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Engine/ZipStreamEngineInstallationData.cs b/src/Tgstation.Server.Host/Components/Engine/ZipStreamEngineInstallationData.cs
index 116689f8de..195c2b3b04 100644
--- a/src/Tgstation.Server.Host/Components/Engine/ZipStreamEngineInstallationData.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/ZipStreamEngineInstallationData.cs
@@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Components.Engine
public ValueTask DisposeAsync() => zipStream.DisposeAsync();
///
- public Task ExtractToPath(string path, CancellationToken cancellationToken)
+ public ValueTask ExtractToPath(string path, CancellationToken cancellationToken)
=> ioManager.ZipToDirectory(path, zipStream, cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
index 68b6aac015..3ec8aef858 100644
--- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
@@ -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);
///
- 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);
+ }
+ }
///
public bool PathContainsParentAccess(string path) => path
diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs
index 3b81869fb8..6d505faec0 100644
--- a/src/Tgstation.Server.Host/IO/IIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/IIOManager.cs
@@ -238,8 +238,8 @@ namespace Tgstation.Server.Host.IO
/// The path to unzip to.
/// The of the . Must have set to . Will be read completely and left open. will be indeterminate.
/// The for the operation.
- /// A representing the running operation.
- Task ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken);
+ /// A representing the running operation.
+ ValueTask ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken);
///
/// Get the of when a given was last modified.