rar5: support ArchiveHeader, additional rar5 header implementation

This commit is contained in:
coderb
2017-12-16 20:39:53 -05:00
parent c346a4ca94
commit 9f549b98da
11 changed files with 239 additions and 62 deletions

View File

@@ -33,11 +33,11 @@ namespace SharpCompress.Archives.Rar
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
yield return part;
if (!part.ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME))
ArchiveHeader ah = part.ArchiveHeader;
if (!ah.IsVolume)
{
yield break; //if file isn't volume then there is no reason to look
}
ArchiveHeader ah = part.ArchiveHeader;
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart);
//we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume
while (fileInfo != null && fileInfo.Exists)
@@ -55,7 +55,7 @@ namespace SharpCompress.Archives.Rar
{
return null;
}
bool oldNumbering = !ah.ArchiveHeaderFlags.HasFlag(ArchiveFlags.NEWNUMBERING)
bool oldNumbering = ah.OldNumberingFormat
|| currentFilePart.MarkHeader.OldNumberingFormat;
if (oldNumbering)
{

View File

@@ -1,3 +1,4 @@
using System;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers
@@ -7,24 +8,91 @@ namespace SharpCompress.Common.Rar.Headers
internal const short mainHeaderSizeWithEnc = 7;
internal const short mainHeaderSize = 6;
public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader) {
}
protected override void ReadFromReader(MarkingBinaryReader reader)
{
HighPosAv = reader.ReadInt16();
PosAv = reader.ReadInt32();
if (ArchiveHeaderFlags.HasFlag(ArchiveFlags.ENCRYPTVER))
if (this.isRar5)
{
EncryptionVersion = reader.ReadByte();
ArchiveHeaderFlags = reader.ReadRarVIntUInt16();
if ((ArchiveHeaderFlags & ArchiveFlagsV5.HasVolumeNumber) == ArchiveFlagsV5.HasVolumeNumber)
{
VolumeNumber = (int)reader.ReadRarVIntUInt32();
}
if (ExtraSize != 0) {
ReadLocator(reader);
}
} else {
ArchiveHeaderFlags = Flags;
HighPosAv = reader.ReadInt16();
PosAv = reader.ReadInt32();
if ((ArchiveHeaderFlags & ArchiveFlagsV4.EncryptVer) == ArchiveFlagsV4.EncryptVer)
{
EncryptionVersion = reader.ReadByte();
}
}
}
internal ArchiveFlags ArchiveHeaderFlags => (ArchiveFlags)Flags;
private void ReadLocator(MarkingBinaryReader reader) {
var size = reader.ReadRarVIntUInt16();
var type = reader.ReadRarVIntUInt16();
if (type != 1) throw new InvalidFormatException("expected locator record");
var flags = reader.ReadRarVIntUInt16();
const ushort HasQuickOpenOffset = 0x01;
const ushort HasRecoveryOffset = 0x02;
ulong quickOpenOffset = 0;
if ((flags & HasQuickOpenOffset) == HasQuickOpenOffset) {
quickOpenOffset = reader.ReadRarVInt();
}
ulong recoveryOffset = 0;
if ((flags & HasRecoveryOffset) == HasRecoveryOffset) {
recoveryOffset = reader.ReadRarVInt();
}
}
internal ushort ArchiveHeaderFlags { get; private set; }
internal int VolumeNumber { get; private set; }
internal short HighPosAv { get; private set; }
internal int PosAv { get; private set; }
internal byte EncryptionVersion { get; private set; }
private byte EncryptionVersion { get; set; }
public bool HasPassword => ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD);
public bool HasPassword => (ArchiveHeaderFlags & ArchiveFlagsV4.Password) == ArchiveFlagsV4.Password;
public bool OldNumberingFormat {
get {
return !this.isRar5 &&
((ArchiveHeaderFlags & ArchiveFlagsV4.NewNumbering) != ArchiveFlagsV4.NewNumbering);
}
}
public bool IsVolume {
get {
return this.isRar5
? ((ArchiveHeaderFlags & ArchiveFlagsV5.Volume) == ArchiveFlagsV5.Volume)
: ((ArchiveHeaderFlags & ArchiveFlagsV4.Volume) == ArchiveFlagsV4.Volume);
}
}
public bool IsFirstVolume {
get {
return this.isRar5
? VolumeNumber == 1
: ((ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume);
}
}
public bool IsSolid {
get {
return this.isRar5
? ((ArchiveHeaderFlags & ArchiveFlagsV5.Solid) == ArchiveFlagsV5.Solid)
: ((ArchiveHeaderFlags & ArchiveFlagsV4.Solid) == ArchiveFlagsV4.Solid);
}
}
}
}

View File

@@ -42,7 +42,7 @@ namespace SharpCompress.Common.Rar.Headers
highUncompressedkSize = int.MaxValue;
}
}
CompressedSize = UInt32To64(highCompressedSize, AdditionalSize);
CompressedSize = UInt32To64(highCompressedSize, AdditionalDataSize);
UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize);
nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize;

View File

@@ -2,8 +2,14 @@ using System;
namespace SharpCompress.Common.Rar.Headers
{
internal enum HeaderType
internal enum HeaderType : byte
{
Rar5ArchiveHeader = 0x01,
Rar5FileHeader = 0x02,
Rar5ServiceHeader = 0x03,
Rar5EncryptionHeader = 0x04,
Rar5EndArchiveHeader = 0x05,
MarkHeader = 0x72,
ArchiveHeader = 0x73,
FileHeader = 0x74,
@@ -16,24 +22,38 @@ namespace SharpCompress.Common.Rar.Headers
EndArchiveHeader = 0x7b
}
internal enum HeaderFlags : short
internal static class HeaderFlagsV4
{
LONG_BLOCK = -0x8000
public const ushort HasData = 0x8000;
}
[Flags]
internal enum ArchiveFlags
internal static class HeaderFlagsV5
{
VOLUME = 0x0001,
COMMENT = 0x0002,
LOCK = 0x0004,
SOLID = 0x0008,
NEWNUMBERING = 0x0010,
AV = 0x0020,
PROTECT = 0x0040,
PASSWORD = 0x0080,
FIRSTVOLUME = 0x0100,
ENCRYPTVER = 0x0200
public const ushort HasExtra = 0x01;
public const ushort HasData = 0x02;
}
internal static class ArchiveFlagsV4
{
public const ushort Volume = 0x0001;
public const ushort Comment = 0x0002;
public const ushort Lock = 0x0004;
public const ushort Solid = 0x0008;
public const ushort NewNumbering = 0x0010;
public const ushort AV = 0x0020;
public const ushort Protect = 0x0040;
public const ushort Password = 0x0080;
public const ushort FirstVolume = 0x0100;
public const ushort EncryptVer = 0x0200;
}
internal static class ArchiveFlagsV5
{
public const ushort Volume = 0x0001;
public const ushort HasVolumeNumber = 0x0002;
public const ushort Solid = 0x0004;
public const ushort Protect = 0x0008;
public const ushort Lock = 0x0010;
}
internal enum HostOS

View File

@@ -8,6 +8,7 @@ namespace SharpCompress.Common.Rar.Headers
private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136
internal bool OldNumberingFormat { get; private set; }
public bool IsRar5 { get; private set; }
private MarkHeader(bool isRar5) {

View File

@@ -13,7 +13,7 @@ namespace SharpCompress.Common.Rar.Headers
Mark = reader.ReadBytes(8);
}
internal uint DataSize => AdditionalSize;
internal uint DataSize => AdditionalDataSize;
internal byte Version { get; private set; }
internal ushort RecSectors { get; private set; }
internal uint TotalBlocks { get; private set; }

View File

@@ -6,8 +6,32 @@ namespace SharpCompress.Common.Rar.Headers
{
internal class RarHeader : IRarHeader
{
internal const short BaseBlockSize = 7;
internal const short LONG_BLOCK = -0x8000;
protected readonly bool isRar5;
protected RarHeader() {
//TODO
//throw new NotImplementedException();
}
private RarHeader(bool isRar5)
{
this.isRar5 = isRar5;
}
protected RarHeader(RarHeader header, RarCrcBinaryReader reader) {
this.isRar5 = header.isRar5;
FillBase(header);
ReadFromReader(reader);
ReadBytes = reader.CurrentReadByteCount;
int headerSizeDiff = HeaderSize - (int)ReadBytes;
if (headerSizeDiff > 0)
{
reader.ReadBytes(headerSizeDiff);
}
VerifyHeaderCrc(reader.GetCrc32());
}
private void FillBase(RarHeader baseHeader)
{
@@ -15,18 +39,18 @@ namespace SharpCompress.Common.Rar.Headers
HeaderType = baseHeader.HeaderType;
Flags = baseHeader.Flags;
HeaderSize = baseHeader.HeaderSize;
AdditionalSize = baseHeader.AdditionalSize;
ExtraSize = baseHeader.ExtraSize;
AdditionalDataSize = baseHeader.AdditionalDataSize;
ReadBytes = baseHeader.ReadBytes;
ArchiveEncoding = baseHeader.ArchiveEncoding;
}
internal static RarHeader Create(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding)
internal static RarHeader ReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5)
{
try
{
var header = new RarHeader();
var header = new RarHeader(isRar5);
header.ArchiveEncoding = archiveEncoding;
reader.Mark();
header.ReadStartFromReader(reader);
header.ReadBytes += reader.CurrentReadByteCount;
return header;
@@ -39,14 +63,33 @@ namespace SharpCompress.Common.Rar.Headers
private void ReadStartFromReader(RarCrcBinaryReader reader)
{
HeadCRC = reader.ReadUInt16();
reader.ResetCrc();
HeaderType = (HeaderType)reader.ReadByte();
Flags = reader.ReadInt16();
HeaderSize = reader.ReadInt16();
if (FlagUtility.HasFlag(Flags, LONG_BLOCK))
if (this.isRar5)
{
AdditionalSize = reader.ReadUInt32();
HeadCRC = reader.ReadUInt32();
reader.ResetCrc();
HeaderSize = (int)reader.ReadRarVIntUInt32(3);
reader.Mark();
HeaderType = (HeaderType)reader.ReadRarVIntUInt32(1);
Flags = reader.ReadRarVIntUInt16(2);
if ((Flags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra)
{
ExtraSize = reader.ReadRarVIntUInt32();
}
if ((Flags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData)
{
AdditionalDataSize = reader.ReadRarVIntUInt32();
}
} else {
reader.Mark();
HeadCRC = reader.ReadUInt16();
reader.ResetCrc();
HeaderType = (HeaderType)reader.ReadByte();
Flags = reader.ReadUInt16();
HeaderSize = reader.ReadInt16();
if ((Flags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData)
{
AdditionalDataSize = reader.ReadUInt32();
}
}
}
@@ -54,16 +97,14 @@ namespace SharpCompress.Common.Rar.Headers
{
throw new NotImplementedException();
}
//x
internal T PromoteHeader<T>(RarCrcBinaryReader reader)
where T : RarHeader, new()
{
T header = new T();
header.FillBase(this);
reader.Mark();
header.ReadFromReader(reader);
header.ReadBytes += reader.CurrentReadByteCount;
header.ReadBytes = reader.CurrentReadByteCount;
int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes;
if (headerSizeDiff > 0)
@@ -71,16 +112,17 @@ namespace SharpCompress.Common.Rar.Headers
reader.ReadBytes(headerSizeDiff);
}
VerifyHeaderCrc(reader.GetCrc());
VerifyHeaderCrc(reader.GetCrc32());
return header;
}
private void VerifyHeaderCrc(ushort crc)
private void VerifyHeaderCrc(uint crc32)
{
if (HeaderType != HeaderType.MarkHeader)
{
if (crc != HeadCRC)
var b = (this.isRar5 ? crc32 : (ushort)crc32) == HeadCRC;
if (!b)
{
throw new InvalidFormatException("rar header crc mismatch");
}
@@ -96,22 +138,27 @@ namespace SharpCompress.Common.Rar.Headers
/// </summary>
protected long ReadBytes { get; private set; }
protected ushort HeadCRC { get; private set; }
protected uint HeadCRC { get; private set; }
public HeaderType HeaderType { get; private set; }
/// <summary>
/// Untyped flags. These should be typed when Promoting to another header
/// </summary>
protected short Flags { get; private set; }
protected ushort Flags { get; private set; }
protected short HeaderSize { get; private set; }
protected int HeaderSize { get; private set; }
internal ArchiveEncoding ArchiveEncoding { get; private set; }
/// <summary>
/// This additional size of the header could be file data
/// Extra header data.
/// </summary>
protected uint AdditionalSize { get; private set; }
protected uint ExtraSize { get; private set; }
/// <summary>
/// Additional data size, eg recovery record
/// </summary>
protected uint AdditionalDataSize { get; private set; }
}
}

View File

@@ -54,16 +54,18 @@ namespace SharpCompress.Common.Rar.Headers
#endif
}
var header = RarHeader.Create(reader, Options.ArchiveEncoding);
var header = RarHeader.ReadBase(reader, Options.ArchiveEncoding, this.isRar5);
if (header == null)
{
return null;
}
switch (header.HeaderType)
{
case HeaderType.Rar5ArchiveHeader:
case HeaderType.ArchiveHeader:
{
var ah = header.PromoteHeader<ArchiveHeader>(reader);
var ah = new ArchiveHeader(header, reader);
//x var ah = header.PromoteHeader<ArchiveHeader>(reader);
IsEncrypted = ah.HasPassword;
return ah;
}

View File

@@ -10,9 +10,9 @@ namespace SharpCompress.Common.Rar {
{
}
public ushort GetCrc()
public uint GetCrc32()
{
return (ushort)~currentCrc;
return ~currentCrc;
}
public void ResetCrc()

View File

@@ -81,7 +81,8 @@ namespace SharpCompress.Common.Rar
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.FIRSTVOLUME);
//return (ArchiveHeader.ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume;
return ArchiveHeader.IsFirstVolume;
}
}
@@ -93,7 +94,7 @@ namespace SharpCompress.Common.Rar
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME);
return ArchiveHeader.IsVolume;
}
}
@@ -106,7 +107,7 @@ namespace SharpCompress.Common.Rar
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.SOLID);
return ArchiveHeader.IsSolid;
}
}
}

View File

@@ -143,18 +143,56 @@ namespace SharpCompress.IO
int shift = 0;
ulong result = 0;
do {
ulong n = ReadByte();
byte b0 = ReadByte();
var b1 = b0 & 0x7f;
ulong n = (ulong)b1;
result |= n << shift;
shift += 7;
if ((n & 0x80) == 0) {
if (b0 == b1) {
return result;
}
// note: we're actually only allowing a max high bit of 2^56
// to avoid an extra complex check for shift overflow due to the
// 10th byte having high bits set
shift += 7;
} while (shift < 63);
throw new FormatException("malformed vint");
}
public uint ReadRarVIntUInt32(int maxBytes = 5) {
int shift = 0;
uint result = 0;
do {
byte b0 = ReadByte();
var b1 = b0 & 0x7f;
uint n = (uint)b1;
result |= n << shift;
if (b0 == b1) {
return result;
}
shift += 7;
// NOTE: we are too strict here but handling the full range adds complexity and we don't need it
} while (shift < 28 && --maxBytes > 0);
throw new FormatException("malformed vint");
}
public ushort ReadRarVIntUInt16(int maxBytes = 3) {
int shift = 0;
uint result = 0;
do {
byte b0 = ReadByte();
var b1 = b0 & 0x7f;
uint n = (uint)b1;
result |= n << shift;
if (b0 == b1) {
return checked((ushort)result);
}
shift += 7;
// NOTE: we are too strict here but handling the full range adds complexity and we don't need it
} while (shift < 14 && --maxBytes > 0);
throw new FormatException("malformed vint");
}
}
}