Merge pull request #682 from adamhathcock/RarFileVolIdx_RarArcVer_GzCrc

This commit is contained in:
Adam Hathcock 2022-07-16 11:29:48 +01:00 committed by GitHub
commit c73a8cb18f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 231 additions and 70 deletions

View file

@ -89,7 +89,8 @@ namespace SharpCompress.Archives.GZip
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream srcStream)
{
srcStream.LoadAllParts();
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions));
int idx = 0;
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
}
public static bool IsGZipFile(string filePath)
{

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common.Rar;
@ -13,8 +13,8 @@ namespace SharpCompress.Archives.Rar
/// </summary>
internal class FileInfoRarArchiveVolume : RarVolume
{
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options)
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options))
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
{
FileInfo = fileInfo;
FileParts = GetVolumeFileParts().ToArray().ToReadOnly();

View file

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Archives.Rar
@ -6,7 +6,7 @@ namespace SharpCompress.Archives.Rar
internal sealed class FileInfoRarFilePart : SeekableFilePart
{
internal FileInfoRarFilePart(FileInfoRarArchiveVolume volume, string? password, MarkHeader mh, FileHeader fh, FileInfo fi)
: base(mh, fh, volume.Stream, password)
: base(mh, fh, volume.Index, volume.Stream, password)
{
FileInfo = fi;
}

View file

@ -37,16 +37,17 @@ namespace SharpCompress.Archives.Rar
{
base.SrcStream.LoadAllParts(); //request all streams
Stream[] streams = base.SrcStream.Streams.ToArray();
int idx = 0;
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
{
base.SrcStream.IsVolumes = true;
streams[1].Position = 0;
base.SrcStream.Position = 0;
return srcStream.Streams.Select(a => new StreamRarArchiveVolume(a, ReaderOptions));
return srcStream.Streams.Select(a => new StreamRarArchiveVolume(a, ReaderOptions, idx++));
}
else //split mode or single file
return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions).AsEnumerable();
return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable();
}
protected override IReader CreateReaderForSolidExtraction()
@ -58,6 +59,9 @@ namespace SharpCompress.Archives.Rar
public override bool IsSolid => Volumes.First().IsSolidArchive;
public virtual int MinVersion => Volumes.First().MinVersion;
public virtual int MaxVersion => Volumes.First().MaxVersion;
#region Creation
/// <summary>
/// Constructor with a FileInfo object to an existing file.

View file

@ -86,4 +86,4 @@ namespace SharpCompress.Archives.Rar
}
}
}
}
}

View file

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@ -9,8 +9,8 @@ namespace SharpCompress.Archives.Rar
private readonly Stream stream;
private readonly string? password;
internal SeekableFilePart(MarkHeader mh, FileHeader fh, Stream stream, string? password)
: base(mh, fh)
internal SeekableFilePart(MarkHeader mh, FileHeader fh, int index, Stream stream, string? password)
: base(mh, fh, index)
{
this.stream = stream;
this.password = password;
@ -28,4 +28,4 @@ namespace SharpCompress.Archives.Rar
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@ -9,8 +9,8 @@ namespace SharpCompress.Archives.Rar
{
internal class StreamRarArchiveVolume : RarVolume
{
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options)
: base(StreamingMode.Seekable, stream, options)
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0)
: base(StreamingMode.Seekable, stream, options, index)
{
}
@ -21,7 +21,7 @@ namespace SharpCompress.Archives.Rar
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
{
return new SeekableFilePart(markHeader, fileHeader, Stream, ReaderOptions.Password);
return new SeekableFilePart(markHeader, fileHeader, this.Index, Stream, ReaderOptions.Password);
}
}
}
}

View file

@ -85,7 +85,8 @@ namespace SharpCompress.Archives.SevenZip
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream srcStream)
{
base.SrcStream.LoadAllParts(); //request all streams
return new SevenZipVolume(srcStream, ReaderOptions).AsEnumerable(); //simple single volume or split, multivolume not supported
int idx = 0;
return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
}
public static bool IsSevenZipFile(string filePath)

View file

@ -107,7 +107,8 @@ namespace SharpCompress.Archives.Tar
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream srcStream)
{
base.SrcStream.LoadAllParts(); //request all streams
return new TarVolume(srcStream, ReaderOptions).AsEnumerable(); //simple single volume or split, multivolume not supported
int idx = 0;
return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
}
/// <summary>

View file

@ -168,6 +168,7 @@ namespace SharpCompress.Archives.Zip
base.SrcStream.Position = 0;
List<Stream> streams = base.SrcStream.Streams.ToList();
int idx = 0;
if (streams.Count > 1) //test part 2 - true = multipart not split
{
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
@ -182,12 +183,12 @@ namespace SharpCompress.Archives.Zip
streams.Add(tmp);
//streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
return streams.Select(a => new ZipVolume(a, ReaderOptions));
return streams.Select(a => new ZipVolume(a, ReaderOptions, idx++));
}
}
//split mode or single file
return new ZipVolume(base.SrcStream, ReaderOptions).AsEnumerable();
return new ZipVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable();
}
internal ZipArchive()

View file

@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SharpCompress.Common
{
@ -70,6 +71,8 @@ namespace SharpCompress.Common
/// </summary>
public abstract bool IsSplitAfter { get; }
public int VolumeIndexFirst => this.Parts?.FirstOrDefault()?.Index ?? 0;
public int VolumeIndexLast => this.Parts?.LastOrDefault()?.Index ?? 0;
/// <inheritdoc/>
public override string ToString() => Key;

View file

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
namespace SharpCompress.Common
{
@ -12,6 +12,7 @@ namespace SharpCompress.Common
internal ArchiveEncoding ArchiveEncoding { get; }
internal abstract string FilePartName { get; }
public int Index { get; set; }
internal abstract Stream GetCompressedStream();
internal abstract Stream? GetRawStream();

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@ -31,8 +31,8 @@ namespace SharpCompress.Common.GZip
internal long EntryStartPosition { get; }
internal DateTime? DateModified { get; private set; }
internal int? Crc { get; private set; }
internal int? UncompressedSize { get; private set; }
internal uint? Crc { get; private set; }
internal uint? UncompressedSize { get; private set; }
internal override string FilePartName => _name!;
@ -52,8 +52,8 @@ namespace SharpCompress.Common.GZip
Span<byte> trailer = stackalloc byte[8];
int n = _stream.Read(trailer);
Crc = BinaryPrimitives.ReadInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4));
Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4));
}
private void ReadAndValidateGzipHeader()

View file

@ -1,12 +1,12 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.GZip
{
public class GZipVolume : Volume
{
public GZipVolume(Stream stream, ReaderOptions options)
: base(stream, options)
public GZipVolume(Stream stream, ReaderOptions options, int index = 0)
: base(stream, options, index)
{
}
@ -20,4 +20,4 @@ namespace SharpCompress.Common.GZip
public override bool IsMultiVolume => true;
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using System;
using System.Collections.Generic;
namespace SharpCompress.Common
{
@ -15,9 +16,11 @@ namespace SharpCompress.Common
bool IsEncrypted { get; }
bool IsSplitAfter { get; }
bool IsSolid { get; }
int VolumeIndexFirst { get; }
int VolumeIndexLast { get; }
DateTime? LastAccessedTime { get; }
DateTime? LastModifiedTime { get; }
long Size { get; }
int? Attrib { get; }
}
}
}

View file

@ -1,8 +1,11 @@
using System;
using System;
namespace SharpCompress.Common
{
public interface IVolume : IDisposable
{
int Index { get; }
string FileName { get; }
}
}
}

View file

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar
@ -8,11 +8,12 @@ namespace SharpCompress.Common.Rar
/// </summary>
internal abstract class RarFilePart : FilePart
{
internal RarFilePart(MarkHeader mh, FileHeader fh)
internal RarFilePart(MarkHeader mh, FileHeader fh, int index)
: base(fh.ArchiveEncoding)
{
MarkHeader = mh;
FileHeader = fh;
Index = index;
}
internal MarkHeader MarkHeader { get; }
@ -24,4 +25,4 @@ namespace SharpCompress.Common.Rar
return null;
}
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -14,9 +14,10 @@ namespace SharpCompress.Common.Rar
public abstract class RarVolume : Volume
{
private readonly RarHeaderFactory _headerFactory;
internal int _maxCompressionAlgorithm;
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options)
: base(stream, options)
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0)
: base(stream, options, index)
{
_headerFactory = new RarHeaderFactory(mode, options);
}
@ -51,6 +52,8 @@ namespace SharpCompress.Common.Rar
case HeaderType.File:
{
var fh = (FileHeader)header;
if (_maxCompressionAlgorithm < fh.CompressionAlgorithm)
_maxCompressionAlgorithm = fh.CompressionAlgorithm;
yield return CreateFilePart(lastMarkHeader!, fh);
}
break;
@ -110,5 +113,37 @@ namespace SharpCompress.Common.Rar
return ArchiveHeader.IsSolid;
}
}
public int MinVersion
{
get
{
EnsureArchiveHeaderLoaded();
if (_maxCompressionAlgorithm >= 50)
return 5; //5-6
else if (_maxCompressionAlgorithm >= 29)
return 3; //3-4
else if (_maxCompressionAlgorithm >= 20)
return 2; //2
else
return 1;
}
}
public int MaxVersion
{
get
{
EnsureArchiveHeaderLoaded();
if (_maxCompressionAlgorithm >= 50)
return 6; //5-6
else if (_maxCompressionAlgorithm >= 29)
return 4; //3-4
else if (_maxCompressionAlgorithm >= 20)
return 2; //2
else
return 1;
}
}
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Linq;
using SharpCompress.IO;
@ -26,7 +26,6 @@ namespace SharpCompress.Common.SevenZip
internal CFileItem Header { get; }
internal CFolder? Folder { get; }
internal int Index { get; }
internal override string FilePartName => Header.Name;
@ -105,4 +104,4 @@ namespace SharpCompress.Common.SevenZip
internal bool IsEncrypted => Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
}
}
}

View file

@ -1,13 +1,13 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.SevenZip
{
public class SevenZipVolume : Volume
{
public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions)
: base(stream, readerFactoryOptions)
public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions, int index = 0)
: base(stream, readerFactoryOptions, index)
{
}
}
}
}

View file

@ -1,13 +1,13 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.Tar
{
public class TarVolume : Volume
{
public TarVolume(Stream stream, ReaderOptions readerOptions)
: base(stream, readerOptions)
public TarVolume(Stream stream, ReaderOptions readerOptions, int index = 0)
: base(stream, readerOptions, index)
{
}
}
}
}

View file

@ -9,8 +9,9 @@ namespace SharpCompress.Common
{
private readonly Stream _actualStream;
internal Volume(Stream stream, ReaderOptions readerOptions)
internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0)
{
Index = index;
ReaderOptions = readerOptions;
if (readerOptions.LeaveStreamOpen)
{
@ -29,6 +30,10 @@ namespace SharpCompress.Common
/// </summary>
public virtual bool IsFirstVolume => true;
public virtual int Index { get; internal set; }
public string FileName { get { return (_actualStream as FileStream)?.Name!; } }
/// <summary>
/// RarArchive is part of a multi-part archive.
/// </summary>

View file

@ -1,15 +1,15 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.Zip
{
public class ZipVolume : Volume
{
public ZipVolume(Stream stream, ReaderOptions readerOptions)
: base(stream, readerOptions)
public ZipVolume(Stream stream, ReaderOptions readerOptions, int index = 0)
: base(stream, readerOptions, index)
{
}
public string? Comment { get; internal set; }
}
}
}

View file

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@ -6,8 +6,8 @@ namespace SharpCompress.Readers.Rar
{
internal class NonSeekableStreamFilePart : RarFilePart
{
internal NonSeekableStreamFilePart(MarkHeader mh, FileHeader fh)
: base(mh, fh)
internal NonSeekableStreamFilePart(MarkHeader mh, FileHeader fh, int index = 0)
: base(mh, fh, index)
{
}
@ -18,4 +18,4 @@ namespace SharpCompress.Readers.Rar
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@ -8,14 +8,14 @@ namespace SharpCompress.Readers.Rar
{
public class RarReaderVolume : RarVolume
{
internal RarReaderVolume(Stream stream, ReaderOptions options)
: base(StreamingMode.Streaming, stream, options)
internal RarReaderVolume(Stream stream, ReaderOptions options, int index = 0)
: base(StreamingMode.Streaming, stream, options, index)
{
}
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
{
return new NonSeekableStreamFilePart(markHeader, fileHeader);
return new NonSeekableStreamFilePart(markHeader, fileHeader, this.Index);
}
internal override IEnumerable<RarFilePart> ReadFileParts()
@ -23,4 +23,4 @@ namespace SharpCompress.Readers.Rar
return GetVolumeFileParts();
}
}
}
}

View file

@ -173,6 +173,38 @@ namespace SharpCompress.Test
VerifyFiles();
}
protected void ArchiveOpenEntryVolumeIndexTest(int[][] results, ReaderOptions readerOptions = null, params string[] testArchives)
{
ArchiveOpenEntryVolumeIndexTest(results, readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)));
}
protected void ArchiveOpenEntryVolumeIndexTest(int[][] results, ReaderOptions readerOptions, IEnumerable<string> testArchives)
{
string[] src = testArchives.ToArray();
using (var archive = ArchiveFactory.Open(testArchives.Select(f => new FileInfo(f)), null))
{
try
{
int idx = 0;
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Assert.Equal(entry.VolumeIndexFirst, results[idx][0]);
Assert.Equal(entry.VolumeIndexLast, results[idx][1]);
Assert.Equal(src[entry.VolumeIndexFirst], archive.Volumes.First(a => a.Index == entry.VolumeIndexFirst).FileName);
Assert.Equal(src[entry.VolumeIndexLast], archive.Volumes.First(a => a.Index == entry.VolumeIndexLast).FileName);
idx++;
}
}
catch (IndexOutOfRangeException)
{
throw;
}
}
}
protected void ArchiveFileRead(string testArchive, ReaderOptions readerOptions = null)
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
@ -107,5 +107,21 @@ namespace SharpCompress.Test.GZip
Assert.Equal(size, tarStream.Length);
}
}
[Fact]
public void TestGzCrcWithMostSignificaltBitNotNegative()
{
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
{
using (var archive = GZipArchive.Open(stream))
{
//process all entries in solid archive until the one we want to test
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL);
}
}
}
}
}
}

View file

@ -435,6 +435,42 @@ namespace SharpCompress.Test.Rar
ArchiveFileRead("Rar2.rar");
}
[Fact]
public void Rar2_ArchiveVersionTest()
{
string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar2.rar");
using (var archive = RarArchive.Open(testArchive))
{
Assert.Equal(2, archive.MinVersion);
Assert.Equal(2, archive.MaxVersion);
}
}
[Fact]
public void Rar4_ArchiveVersionTest()
{
string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar4.multi.part01.rar");
using (var archive = RarArchive.Open(testArchive))
{
Assert.Equal(3, archive.MinVersion);
Assert.Equal(4, archive.MaxVersion);
}
}
[Fact]
public void Rar5_ArchiveVersionTest()
{
string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar");
using (var archive = RarArchive.Open(testArchive))
{
Assert.Equal(5, archive.MinVersion);
Assert.Equal(6, archive.MaxVersion);
}
}
[Fact]
public void Rar4_Multi_ArchiveFileRead()
{
@ -575,6 +611,25 @@ namespace SharpCompress.Test.Rar
"Rar4.multi.part07.rar");
}
[Fact]
public void Rar4_Multi_ArchiveOpenEntryVolumeIndexTest()
{
ArchiveOpenEntryVolumeIndexTest(
new[] {
new[] { 0, 1 }, //exe - Rar4.multi.part01.rar to Rar4.multi.part02.rar
new[] { 1, 5 }, //jpg - Rar4.multi.part02.rar to Rar4.multi.part06.rar
new[] { 5, 6 } //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar
},
null,
"Rar4.multi.part01.rar",
"Rar4.multi.part02.rar",
"Rar4.multi.part03.rar",
"Rar4.multi.part04.rar",
"Rar4.multi.part05.rar",
"Rar4.multi.part06.rar",
"Rar4.multi.part07.rar");
}
[Fact]
public void Rar_Multi_ArchiveFileRead()
{