Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/SharpCompress/Archives/IArchiveEntryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ public void WriteToDirectory(
string destinationDirectory,
ExtractionOptions? options = null
) =>
ExtractionMethods.WriteEntryToDirectory(
entry,
entry.WriteEntryToDirectory(
destinationDirectory,
options,
(path) => entry.WriteToFile(path, options)
Expand All @@ -121,9 +120,8 @@ public async ValueTask WriteToDirectoryAsync(
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
await ExtractionMethods
await entry
.WriteEntryToDirectoryAsync(
entry,
destinationDirectory,
options,
async (path, ct) =>
Expand All @@ -136,8 +134,7 @@ await entry.WriteToFileAsync(path, options, ct).ConfigureAwait(false),
/// Extract to specific file
/// </summary>
public void WriteToFile(string destinationFileName, ExtractionOptions? options = null) =>
ExtractionMethods.WriteEntryToFile(
entry,
entry.WriteEntryToFile(
destinationFileName,
options,
(x, fm) =>
Expand All @@ -155,9 +152,8 @@ public async ValueTask WriteToFileAsync(
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
await ExtractionMethods
await entry
.WriteEntryToFileAsync(
entry,
destinationFileName,
options,
async (x, fm, ct) =>
Expand Down
26 changes: 11 additions & 15 deletions src/SharpCompress/Archives/IArchiveExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Readers;

Expand Down Expand Up @@ -39,29 +37,27 @@ private void WriteToDirectoryInternal(
IProgress<ProgressReport>? progress
)
{
options ??= new ExtractionOptions();
var fullDestinationDirectoryPath = DirectoryManagement.GetFullDestinationDirectoryPath(
destinationDirectory
);

var totalBytes = archive.TotalUncompressedSize;
var bytesRead = 0L;
var seenDirectories = new HashSet<string>();

foreach (var entry in archive.Entries)
{
if (entry.IsDirectory)
{
var dirPath = Path.Combine(
destinationDirectory,
entry.Key.NotNull("Entry Key is null")
);
if (
Path.GetDirectoryName(dirPath + "/") is { } parentDirectory
&& seenDirectories.Add(dirPath)
)
{
Directory.CreateDirectory(parentDirectory);
}
entry.WriteEntryToDirectoryCore(fullDestinationDirectoryPath, options, null);
continue;
}

entry.WriteToDirectory(destinationDirectory, options);
entry.WriteEntryToDirectoryCore(
fullDestinationDirectoryPath,
options,
path => entry.WriteToFile(path, options)
);

bytesRead += entry.Size;
progress?.Report(
Expand Down
36 changes: 20 additions & 16 deletions src/SharpCompress/Archives/IAsyncArchiveExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
Expand All @@ -15,7 +13,6 @@ public static class IAsyncArchiveExtensions
/// <summary>
/// Extract to specific directory asynchronously with progress reporting and cancellation support
/// </summary>
/// <param name="archive">The archive to extract.</param>
/// <param name="destinationDirectory">The folder to extract into.</param>
/// <param name="options">Extraction options.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
Expand Down Expand Up @@ -59,32 +56,39 @@ private async ValueTask WriteToDirectoryAsyncInternal(
CancellationToken cancellationToken
)
{
options ??= new ExtractionOptions();
var fullDestinationDirectoryPath = DirectoryManagement.GetFullDestinationDirectoryPath(
destinationDirectory
);

var totalBytes = await archive.TotalUncompressedSizeAsync().ConfigureAwait(false);
var bytesRead = 0L;
var seenDirectories = new HashSet<string>();

await foreach (var entry in archive.EntriesAsync.WithCancellation(cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();

if (entry.IsDirectory)
{
var dirPath = Path.Combine(
destinationDirectory,
entry.Key.NotNull("Entry Key is null")
);
if (
Path.GetDirectoryName(dirPath + "/") is { } parentDirectory
&& seenDirectories.Add(dirPath)
)
{
Directory.CreateDirectory(parentDirectory);
}
await entry
.WriteEntryToDirectoryAsyncCore(
fullDestinationDirectoryPath,
options,
null,
cancellationToken
)
Comment thread
adamhathcock marked this conversation as resolved.
.ConfigureAwait(false);
continue;
}

await entry
.WriteToDirectoryAsync(destinationDirectory, options, cancellationToken)
.WriteEntryToDirectoryAsyncCore(
fullDestinationDirectoryPath,
options,
async (path, ct) =>
await entry.WriteToFileAsync(path, options, ct).ConfigureAwait(false),
cancellationToken
)
.ConfigureAwait(false);

bytesRead += entry.Size;
Expand Down
77 changes: 77 additions & 0 deletions src/SharpCompress/Common/DirectoryManagement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.IO;

namespace SharpCompress.Common;

internal static class DirectoryManagement
{
internal const string CreateDirectoryOutsideDestinationMessage =
"Entry is trying to create a directory outside of the destination directory.";
internal const string WriteFileOutsideDestinationMessage =
"Entry is trying to write a file outside of the destination directory.";

internal static string GetFullDestinationDirectoryPath(string destinationDirectory)
{
var fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);

// Keep the trailing separator so prefix checks cannot match sibling directories.
if (
!IsDirectorySeparator(
fullDestinationDirectoryPath[fullDestinationDirectoryPath.Length - 1]
)
)
{
fullDestinationDirectoryPath += Path.DirectorySeparatorChar;
}

if (!Directory.Exists(fullDestinationDirectoryPath))
{
throw new ExtractionException(
$"Directory does not exist to extract to: {fullDestinationDirectoryPath}"
);
}

return fullDestinationDirectoryPath;
}

internal static void EnsurePathInDestinationDirectory(
string destinationPath,
string fullDestinationDirectoryPath,
string exceptionMessage
)
{
if (destinationPath.StartsWith(fullDestinationDirectoryPath, Utility.PathComparison))
{
return;
}

if (
string.Equals(
destinationPath,
TrimTrailingDirectorySeparators(fullDestinationDirectoryPath),
Utility.PathComparison
)
)
{
return;
}

throw new ExtractionException(exceptionMessage);
}

private static bool IsDirectorySeparator(char value) =>
value == Path.DirectorySeparatorChar || value == Path.AltDirectorySeparatorChar;

private static string TrimTrailingDirectorySeparators(string path)
{
var root = Path.GetPathRoot(path);
var rootLength = root?.Length ?? 0;
var end = path.Length;

while (end > rootLength && IsDirectorySeparator(path[end - 1]))
{
end--;
}

return end == path.Length ? path : path.Substring(0, end);
}
}
117 changes: 0 additions & 117 deletions src/SharpCompress/Common/ExtractionMethods.Async.cs

This file was deleted.

Loading
Loading