Files
sharpcompress/src/SharpCompress/Common/ExtractionMethods.cs

233 lines
7.8 KiB
C#
Raw Normal View History

2022-12-20 15:06:44 +00:00
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
2022-12-20 15:20:49 +00:00
namespace SharpCompress.Common;
internal static class ExtractionMethods
{
/// <summary>
/// Gets the appropriate StringComparison for path checks based on the file system.
/// Windows uses case-insensitive file systems, while Unix-like systems use case-sensitive file systems.
/// </summary>
private static StringComparison PathComparison =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
2022-12-20 15:20:49 +00:00
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteEntryToDirectory(
IEntry entry,
string destinationDirectory,
ExtractionOptions? options,
Action<string, ExtractionOptions?> write
)
{
2022-12-20 15:20:49 +00:00
string destinationFileName;
2024-03-14 08:38:12 +00:00
var fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);
2022-12-20 15:20:49 +00:00
//check for trailing slash.
if (
fullDestinationDirectoryPath[fullDestinationDirectoryPath.Length - 1]
!= Path.DirectorySeparatorChar
2022-12-20 13:45:47 +00:00
)
{
2022-12-20 15:20:49 +00:00
fullDestinationDirectoryPath += Path.DirectorySeparatorChar;
}
2021-10-02 15:29:03 +01:00
2022-12-20 15:20:49 +00:00
if (!Directory.Exists(fullDestinationDirectoryPath))
{
throw new ExtractionException(
$"Directory does not exist to extract to: {fullDestinationDirectoryPath}"
);
}
2021-09-12 08:47:30 +01:00
2022-12-20 15:20:49 +00:00
options ??= new ExtractionOptions() { Overwrite = true };
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
file = Utility.ReplaceInvalidFileNameChars(file);
2022-12-20 15:20:49 +00:00
if (options.ExtractFullPath)
{
2024-04-22 14:17:08 +01:00
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
.NotNull("Directory is null");
2024-03-14 08:38:12 +00:00
var destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder));
2022-12-20 15:20:49 +00:00
if (!Directory.Exists(destdir))
{
if (!destdir.StartsWith(fullDestinationDirectoryPath, PathComparison))
{
2022-12-20 15:20:49 +00:00
throw new ExtractionException(
"Entry is trying to create a directory outside of the destination directory."
);
}
2022-12-20 15:20:49 +00:00
Directory.CreateDirectory(destdir);
}
2022-12-20 15:20:49 +00:00
destinationFileName = Path.Combine(destdir, file);
}
else
{
destinationFileName = Path.Combine(fullDestinationDirectoryPath, file);
}
2022-12-20 15:20:49 +00:00
if (!entry.IsDirectory)
{
destinationFileName = Path.GetFullPath(destinationFileName);
if (!destinationFileName.StartsWith(fullDestinationDirectoryPath, PathComparison))
{
2022-12-20 15:20:49 +00:00
throw new ExtractionException(
"Entry is trying to write a file outside of the destination directory."
);
}
2022-12-20 15:20:49 +00:00
write(destinationFileName, options);
}
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
{
Directory.CreateDirectory(destinationFileName);
}
2022-12-20 15:20:49 +00:00
}
2021-01-09 13:33:34 +00:00
2022-12-20 15:20:49 +00:00
public static void WriteEntryToFile(
IEntry entry,
string destinationFileName,
ExtractionOptions? options,
Action<string, FileMode> openAndWrite
)
{
if (entry.LinkTarget != null)
{
2022-12-20 15:20:49 +00:00
if (options?.WriteSymbolicLink is null)
{
2022-12-20 15:20:49 +00:00
throw new ExtractionException(
"Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null"
);
}
2022-12-20 15:20:49 +00:00
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
}
else
{
2024-03-14 08:38:12 +00:00
var fm = FileMode.Create;
2022-12-20 15:20:49 +00:00
options ??= new ExtractionOptions() { Overwrite = true };
2022-12-20 15:20:49 +00:00
if (!options.Overwrite)
{
fm = FileMode.CreateNew;
}
2022-12-20 15:20:49 +00:00
openAndWrite(destinationFileName, fm);
entry.PreserveExtractionOptions(destinationFileName, options);
}
}
public static async Task WriteEntryToDirectoryAsync(
IEntry entry,
string destinationDirectory,
ExtractionOptions? options,
Func<string, ExtractionOptions?, Task> writeAsync,
CancellationToken cancellationToken = default
)
{
string destinationFileName;
var fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);
//check for trailing slash.
if (
fullDestinationDirectoryPath[fullDestinationDirectoryPath.Length - 1]
!= Path.DirectorySeparatorChar
)
{
fullDestinationDirectoryPath += Path.DirectorySeparatorChar;
}
if (!Directory.Exists(fullDestinationDirectoryPath))
{
throw new ExtractionException(
$"Directory does not exist to extract to: {fullDestinationDirectoryPath}"
);
}
options ??= new ExtractionOptions() { Overwrite = true };
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
file = Utility.ReplaceInvalidFileNameChars(file);
if (options.ExtractFullPath)
{
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
.NotNull("Directory is null");
var destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder));
if (!Directory.Exists(destdir))
{
if (!destdir.StartsWith(fullDestinationDirectoryPath, PathComparison))
{
throw new ExtractionException(
"Entry is trying to create a directory outside of the destination directory."
);
}
Directory.CreateDirectory(destdir);
}
destinationFileName = Path.Combine(destdir, file);
}
else
{
destinationFileName = Path.Combine(fullDestinationDirectoryPath, file);
}
if (!entry.IsDirectory)
{
destinationFileName = Path.GetFullPath(destinationFileName);
if (!destinationFileName.StartsWith(fullDestinationDirectoryPath, PathComparison))
{
throw new ExtractionException(
"Entry is trying to write a file outside of the destination directory."
);
}
await writeAsync(destinationFileName, options).ConfigureAwait(false);
}
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
{
Directory.CreateDirectory(destinationFileName);
}
}
public static async Task WriteEntryToFileAsync(
IEntry entry,
string destinationFileName,
ExtractionOptions? options,
Func<string, FileMode, Task> openAndWriteAsync,
CancellationToken cancellationToken = default
)
{
if (entry.LinkTarget != null)
{
if (options?.WriteSymbolicLink is null)
{
throw new ExtractionException(
"Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null"
);
}
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
}
else
{
var fm = FileMode.Create;
options ??= new ExtractionOptions() { Overwrite = true };
if (!options.Overwrite)
{
fm = FileMode.CreateNew;
}
await openAndWriteAsync(destinationFileName, fm).ConfigureAwait(false);
entry.PreserveExtractionOptions(destinationFileName, options);
}
}
2022-12-20 13:45:47 +00:00
}