mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
ReadExactAsync throws IncompleteArchiveException, so the async decrypt path's EndOfStreamException catch never ran. On net48, EntryStream.Dispose skips the entry through the async path, so its IncompleteArchiveException replaced the InvalidFormatException from the synchronous read and failed Rar_Multi_Reader_Encrypted. Sync and async now report truncated encrypted data as InvalidFormatException. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
417 lines
14 KiB
C#
417 lines
14 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using SharpCompress.Archives.Rar;
|
|
using SharpCompress.Common;
|
|
using SharpCompress.Readers;
|
|
using SharpCompress.Readers.Rar;
|
|
using SharpCompress.Test.Mocks;
|
|
using Xunit;
|
|
|
|
namespace SharpCompress.Test.Rar;
|
|
|
|
public class RarReaderAsyncTests : ReaderTests
|
|
{
|
|
[Theory]
|
|
[InlineData("Rar15.rar")]
|
|
[InlineData("Rar.rar")]
|
|
[InlineData("Rar.Audio_program.rar")]
|
|
[InlineData("Rar5.rar")]
|
|
[InlineData("Rar5.solid.rar")]
|
|
public async ValueTask Rar_Reader_Async_Uses_Only_Async_Stream_Operations(string filename)
|
|
{
|
|
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename));
|
|
await using var reader = await ReaderFactory.OpenAsyncReader(
|
|
new AsyncOnlyStream(stream),
|
|
new ReaderOptions { LookForHeader = true }
|
|
);
|
|
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
if (!reader.Entry.IsDirectory)
|
|
{
|
|
using var output = new AsyncOnlyStream(new MemoryStream());
|
|
await reader.WriteEntryToAsync(output);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Multi_Reader_Async() =>
|
|
await DoRar_Multi_Reader_Async([
|
|
"Rar.multi.part01.rar",
|
|
"Rar.multi.part02.rar",
|
|
"Rar.multi.part03.rar",
|
|
"Rar.multi.part04.rar",
|
|
"Rar.multi.part05.rar",
|
|
"Rar.multi.part06.rar",
|
|
]);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Multi_Reader_Async() =>
|
|
await DoRar_Multi_Reader_Async([
|
|
"Rar5.multi.part01.rar",
|
|
"Rar5.multi.part02.rar",
|
|
"Rar5.multi.part03.rar",
|
|
"Rar5.multi.part04.rar",
|
|
"Rar5.multi.part05.rar",
|
|
"Rar5.multi.part06.rar",
|
|
]);
|
|
|
|
private async ValueTask DoRar_Multi_Reader_Async(string[] archives)
|
|
{
|
|
using (
|
|
IReader baseReader = RarReader.OpenReader(
|
|
archives
|
|
.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s))
|
|
.Select(p => File.OpenRead(p))
|
|
)
|
|
)
|
|
{
|
|
IAsyncReader reader = (IAsyncReader)baseReader;
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
VerifyFiles();
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Multi_Reader_Encrypted_Async() =>
|
|
await Assert.ThrowsAsync<InvalidFormatException>(async () =>
|
|
{
|
|
string[] archives =
|
|
[
|
|
"Rar.EncryptedParts.part01.rar",
|
|
"Rar.EncryptedParts.part02.rar",
|
|
"Rar.EncryptedParts.part03.rar",
|
|
"Rar.EncryptedParts.part04.rar",
|
|
"Rar.EncryptedParts.part05.rar",
|
|
"Rar.EncryptedParts.part06.rar",
|
|
];
|
|
using (
|
|
IReader baseReader = RarReader.OpenReader(
|
|
archives
|
|
.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s))
|
|
.Select(p => File.OpenRead(p)),
|
|
ReaderOptions.ForExternalStream with
|
|
{
|
|
Password = "test",
|
|
}
|
|
)
|
|
)
|
|
{
|
|
IAsyncReader reader = (IAsyncReader)baseReader;
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
VerifyFiles();
|
|
});
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Multi_Reader_Delete_Files_Async() =>
|
|
await DoRar_Multi_Reader_Delete_Files_Async([
|
|
"Rar.multi.part01.rar",
|
|
"Rar.multi.part02.rar",
|
|
"Rar.multi.part03.rar",
|
|
"Rar.multi.part04.rar",
|
|
"Rar.multi.part05.rar",
|
|
"Rar.multi.part06.rar",
|
|
]);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Multi_Reader_Delete_Files_Async() =>
|
|
await DoRar_Multi_Reader_Delete_Files_Async([
|
|
"Rar5.multi.part01.rar",
|
|
"Rar5.multi.part02.rar",
|
|
"Rar5.multi.part03.rar",
|
|
"Rar5.multi.part04.rar",
|
|
"Rar5.multi.part05.rar",
|
|
"Rar5.multi.part06.rar",
|
|
]);
|
|
|
|
private async ValueTask DoRar_Multi_Reader_Delete_Files_Async(string[] archives)
|
|
{
|
|
foreach (var file in archives)
|
|
{
|
|
File.Copy(
|
|
Path.Combine(TEST_ARCHIVES_PATH, file),
|
|
Path.Combine(SCRATCH2_FILES_PATH, file)
|
|
);
|
|
}
|
|
var streams = archives
|
|
.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))
|
|
.Select(File.OpenRead)
|
|
.ToList();
|
|
using (IReader baseReader = RarReader.OpenReader(streams))
|
|
{
|
|
IAsyncReader reader = (IAsyncReader)baseReader;
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
foreach (var stream in streams)
|
|
{
|
|
stream.Dispose();
|
|
}
|
|
VerifyFiles();
|
|
|
|
foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)))
|
|
{
|
|
File.Delete(file);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_None_Reader_Async() =>
|
|
await ReadAsync("Rar.none.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_None_Reader_Async() =>
|
|
await ReadAsync("Rar5.none.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Reader_Async() => await ReadAsync("Rar.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Reader_Async() => await ReadAsync("Rar5.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_CRC_Blake2_Reader_Async() =>
|
|
await ReadAsync("Rar5.crc_blake2.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_EncryptedFileAndHeader_Reader_Async() =>
|
|
await ReadRar_Async("Rar.encrypted_filesAndHeader.rar", "test");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_EncryptedFileAndHeader_Reader_Async() =>
|
|
await ReadRar_Async("Rar5.encrypted_filesAndHeader.rar", "test");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_EncryptedFileOnly_Reader_Async() =>
|
|
await ReadRar_Async("Rar.encrypted_filesOnly.rar", "test");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_EncryptedFileOnly_Reader_Async() =>
|
|
await ReadRar_Async("Rar5.encrypted_filesOnly.rar", "test");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Encrypted_Reader_Async() =>
|
|
await ReadRar_Async("Rar.Encrypted.rar", "test");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Encrypted_Reader_Async() =>
|
|
await ReadRar_Async("Rar5.encrypted_filesOnly.rar", "test");
|
|
|
|
private async ValueTask ReadRar_Async(string testArchive, string password) =>
|
|
await ReadAsync(
|
|
testArchive,
|
|
CompressionType.Rar,
|
|
ReaderOptions.ForFilePath with
|
|
{
|
|
Password = password,
|
|
}
|
|
);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Entry_Stream_Async() => await DoRar_Entry_Stream_Async("Rar.rar");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Entry_Stream_Async() => await DoRar_Entry_Stream_Async("Rar5.rar");
|
|
|
|
private async ValueTask DoRar_Entry_Stream_Async(string filename)
|
|
{
|
|
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
|
|
await using (var reader = await ReaderFactory.OpenAsyncReader(new AsyncOnlyStream(stream)))
|
|
{
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
if (!reader.Entry.IsDirectory)
|
|
{
|
|
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
|
var entryStream = await reader.OpenEntryStreamAsync();
|
|
try
|
|
{
|
|
var file = Path.GetFileName(reader.Entry.Key).NotNull();
|
|
var folder =
|
|
Path.GetDirectoryName(reader.Entry.Key)
|
|
?? throw new InvalidOperationException(
|
|
"Entry key must have a directory name."
|
|
);
|
|
var destdir = Path.Combine(SCRATCH_FILES_PATH, folder);
|
|
if (!Directory.Exists(destdir))
|
|
{
|
|
Directory.CreateDirectory(destdir);
|
|
}
|
|
var destinationFileName = Path.Combine(destdir, file);
|
|
|
|
using var fs = File.OpenWrite(destinationFileName);
|
|
await entryStream.CopyToAsync(fs);
|
|
}
|
|
finally
|
|
{
|
|
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
|
await entryStream.DisposeAsync();
|
|
#else
|
|
entryStream.Dispose();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
VerifyFiles();
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Reader_Audio_program_Async()
|
|
{
|
|
using (
|
|
var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar"))
|
|
)
|
|
await using (
|
|
var reader = await ReaderFactory.OpenAsyncReader(
|
|
new AsyncOnlyStream(stream),
|
|
ReaderOptions.ForExternalStream with
|
|
{
|
|
LookForHeader = true,
|
|
}
|
|
)
|
|
)
|
|
{
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
CompareFilesByPath(
|
|
Path.Combine(SCRATCH_FILES_PATH, "test.dat"),
|
|
Path.Combine(MISC_TEST_FILES_PATH, "test.dat")
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Jpg_Reader_Async()
|
|
{
|
|
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg")))
|
|
using (
|
|
IReader baseReader = RarReader.OpenReader(
|
|
stream,
|
|
ReaderOptions.ForExternalStream with
|
|
{
|
|
LookForHeader = true,
|
|
}
|
|
)
|
|
)
|
|
{
|
|
IAsyncReader reader = (IAsyncReader)baseReader;
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
VerifyFiles();
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Solid_Reader_Async() =>
|
|
await ReadAsync("Rar.solid.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Comment_Reader_Async() =>
|
|
await ReadAsync("Rar.comment.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Comment_Reader_Async() =>
|
|
await ReadAsync("Rar5.comment.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Solid_Reader_Async() =>
|
|
await ReadAsync("Rar5.solid.rar", CompressionType.Rar);
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Solid_Skip_Reader_Async() =>
|
|
await DoRar_Solid_Skip_Reader_Async("Rar.solid.rar");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Solid_Skip_Reader_Async() =>
|
|
await DoRar_Solid_Skip_Reader_Async("Rar5.solid.rar");
|
|
|
|
private async ValueTask DoRar_Solid_Skip_Reader_Async(string filename)
|
|
{
|
|
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename));
|
|
await using var reader = await ReaderFactory.OpenAsyncReader(
|
|
new AsyncOnlyStream(stream),
|
|
ReaderOptions.ForExternalStream with
|
|
{
|
|
LookForHeader = true,
|
|
}
|
|
);
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
if (reader.Entry.Key.NotNull().Contains("jpg"))
|
|
{
|
|
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask Rar_Reader_Skip_Async() => await DoRar_Reader_Skip_Async("Rar.rar");
|
|
|
|
[Fact]
|
|
public async ValueTask Rar5_Reader_Skip_Async() => await DoRar_Reader_Skip_Async("Rar5.rar");
|
|
|
|
private async ValueTask DoRar_Reader_Skip_Async(string filename)
|
|
{
|
|
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename));
|
|
await using var reader = await ReaderFactory.OpenAsyncReader(
|
|
new AsyncOnlyStream(stream),
|
|
ReaderOptions.ForExternalStream with
|
|
{
|
|
LookForHeader = true,
|
|
}
|
|
);
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
if (reader.Entry.Key.NotNull().Contains("jpg"))
|
|
{
|
|
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async ValueTask ReadAsync(
|
|
string testArchive,
|
|
CompressionType expectedCompression,
|
|
ReaderOptions? readerOptions = null
|
|
)
|
|
{
|
|
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
|
using Stream stream = File.OpenRead(testArchive);
|
|
await using var reader = await ReaderFactory.OpenAsyncReader(
|
|
new AsyncOnlyStream(stream),
|
|
readerOptions ?? ReaderOptions.ForExternalStream
|
|
);
|
|
while (await reader.MoveToNextEntryAsync())
|
|
{
|
|
if (!reader.Entry.IsDirectory)
|
|
{
|
|
Assert.Equal(expectedCompression, reader.Entry.CompressionType);
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
}
|
|
}
|
|
VerifyFiles();
|
|
}
|
|
}
|