Files
sharpcompress/src/SharpCompress/Common/GZip/GZipFilePart.cs

132 lines
3.7 KiB
C#
Raw Normal View History

using System;
using System.Buffers.Binary;
using System.Collections.Generic;
2015-12-30 11:19:42 +00:00
using System.IO;
using SharpCompress.Common.Tar.Headers;
2016-09-26 11:49:49 +01:00
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
namespace SharpCompress.Common.GZip;
internal sealed class GZipFilePart : FilePart
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
private string? _name;
private readonly Stream _stream;
2015-12-30 11:19:42 +00:00
2023-03-21 13:14:08 +00:00
internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding)
: base(archiveEncoding)
2022-12-20 15:06:44 +00:00
{
_stream = stream;
ReadAndValidateGzipHeader();
if (stream.CanSeek)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
var position = stream.Position;
stream.Position = stream.Length - 8;
ReadTrailer();
stream.Position = position;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
EntryStartPosition = stream.Position;
}
internal long EntryStartPosition { get; }
internal DateTime? DateModified { get; private set; }
internal uint? Crc { get; private set; }
internal uint? UncompressedSize { get; private set; }
internal override string? FilePartName => _name;
2022-12-20 15:06:44 +00:00
2022-12-20 15:20:49 +00:00
internal override Stream GetCompressedStream() =>
new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);
2015-12-30 11:19:42 +00:00
2022-12-20 15:20:49 +00:00
internal override Stream GetRawStream() => _stream;
2022-12-20 15:06:44 +00:00
private void ReadTrailer()
{
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
Span<byte> trailer = stackalloc byte[8];
var n = _stream.Read(trailer);
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4));
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
private void ReadAndValidateGzipHeader()
{
// read the header on the first read
Span<byte> header = stackalloc byte[10];
var n = _stream.Read(header);
// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
return;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
if (n != 10)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
throw new ZlibException("Not a valid GZIP stream.");
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
{
2022-12-20 15:06:44 +00:00
throw new ZlibException("Bad GZIP header.");
}
2022-12-20 15:06:44 +00:00
var timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4));
DateModified = TarHeader.EPOCH.AddSeconds(timet);
if ((header[3] & 0x04) == 0x04)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
// read and discard extra field
n = _stream.Read(header.Slice(0, 2)); // 2-byte length field
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
var extraLength = (short)(header[0] + (header[1] * 256));
var extra = new byte[extraLength];
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
if (!_stream.ReadFully(extra))
{
2022-12-20 15:06:44 +00:00
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
}
2022-12-20 15:06:44 +00:00
n = extraLength;
}
if ((header[3] & 0x08) == 0x08)
{
_name = ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x10) == 0x010)
{
ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x02) == 0x02)
{
_stream.ReadByte(); // CRC16, ignore
}
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
private string ReadZeroTerminatedString(Stream stream)
{
Span<byte> buf1 = stackalloc byte[1];
var list = new List<byte>();
var done = false;
do
{
// workitem 7740
var n = stream.Read(buf1);
if (n != 1)
{
2022-12-20 15:06:44 +00:00
throw new ZlibException("Unexpected EOF reading GZIP header.");
}
2022-12-20 15:06:44 +00:00
if (buf1[0] == 0)
{
2022-12-20 15:06:44 +00:00
done = true;
}
2022-12-20 15:06:44 +00:00
else
{
2022-12-20 15:06:44 +00:00
list.Add(buf1[0]);
}
2022-12-20 15:06:44 +00:00
} while (!done);
var buffer = list.ToArray();
return ArchiveEncoding.Decode(buffer);
2015-12-30 11:19:42 +00:00
}
}