You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
3.5 KiB
153 lines
3.5 KiB
package headers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"hash/crc32"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
// SignatureHeader size is the size of the signature header.
|
|
SignatureHeaderSize = 32
|
|
|
|
// MaxHeaderSize is the maximum header size.
|
|
MaxHeaderSize = int64(1 << 62) // 4 exbibyte
|
|
)
|
|
|
|
var (
|
|
// MagicBytes is the magic bytes used in the 7z signature.
|
|
MagicBytes = [6]byte{0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C}
|
|
|
|
// ErrInvalidSignatureHeader is returned when signature header is invalid.
|
|
ErrInvalidSignatureHeader = errors.New("invalid signature header")
|
|
)
|
|
|
|
// SignatureHeader is the structure found at the top of 7z files.
|
|
type SignatureHeader struct {
|
|
Signature [6]byte
|
|
|
|
ArchiveVersion struct {
|
|
Major byte
|
|
Minor byte
|
|
}
|
|
|
|
StartHeaderCRC uint32
|
|
|
|
StartHeader struct {
|
|
NextHeaderOffset int64
|
|
NextHeaderSize int64
|
|
NextHeaderCRC uint32
|
|
}
|
|
}
|
|
|
|
// ReadSignatureHeader reads the signature header.
|
|
func ReadSignatureHeader(r io.Reader) (*SignatureHeader, error) {
|
|
var raw [SignatureHeaderSize]byte
|
|
_, err := r.Read(raw[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var header SignatureHeader
|
|
copy(header.Signature[:], raw[:6])
|
|
if bytes.Compare(header.Signature[:], MagicBytes[:]) != 0 {
|
|
return nil, ErrInvalidSignatureHeader
|
|
}
|
|
|
|
header.ArchiveVersion.Major = raw[6]
|
|
header.ArchiveVersion.Minor = raw[7]
|
|
header.StartHeaderCRC = binary.LittleEndian.Uint32(raw[8:])
|
|
header.StartHeader.NextHeaderOffset = int64(binary.LittleEndian.Uint64(raw[12:]))
|
|
header.StartHeader.NextHeaderSize = int64(binary.LittleEndian.Uint64(raw[20:]))
|
|
header.StartHeader.NextHeaderCRC = binary.LittleEndian.Uint32(raw[28:])
|
|
|
|
if header.StartHeader.NextHeaderSize < 0 || header.StartHeader.NextHeaderSize > MaxHeaderSize {
|
|
return &header, ErrInvalidSignatureHeader
|
|
}
|
|
if crc32.ChecksumIEEE(raw[12:]) != header.StartHeaderCRC {
|
|
err = ErrChecksumMismatch
|
|
}
|
|
return &header, err
|
|
}
|
|
|
|
// Header is structure containing file and stream information.
|
|
type Header struct {
|
|
MainStreamsInfo *StreamsInfo
|
|
FilesInfo []*FileInfo
|
|
}
|
|
|
|
// ReadPackedStreamsForHeaders reads either a header or encoded header structure.
|
|
func ReadPackedStreamsForHeaders(r *io.LimitedReader) (header *Header, encodedHeader *StreamsInfo, err error) {
|
|
id, err := ReadByte(r)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch id {
|
|
case k7zHeader:
|
|
if header, err = ReadHeader(r); err != nil && err != io.EOF {
|
|
return nil, nil, err
|
|
}
|
|
|
|
case k7zEncodedHeader:
|
|
if encodedHeader, err = ReadStreamsInfo(r); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
case k7zEnd:
|
|
if header == nil && encodedHeader == nil {
|
|
return nil, nil, ErrUnexpectedPropertyID
|
|
}
|
|
break
|
|
|
|
default:
|
|
return nil, nil, ErrUnexpectedPropertyID
|
|
}
|
|
|
|
return header, encodedHeader, nil
|
|
}
|
|
|
|
// ReadHeader reads a header structure.
|
|
func ReadHeader(r *io.LimitedReader) (*Header, error) {
|
|
header := &Header{}
|
|
|
|
for {
|
|
id, err := ReadByte(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch id {
|
|
case k7zArchiveProperties:
|
|
return nil, ErrArchivePropertiesNotImplemented
|
|
|
|
case k7zAdditionalStreamsInfo:
|
|
return nil, ErrAdditionalStreamsNotImplemented
|
|
|
|
case k7zMainStreamsInfo:
|
|
if header.MainStreamsInfo, err = ReadStreamsInfo(r); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
case k7zFilesInfo:
|
|
// Limit the maximum amount of FileInfos that get allocated to size
|
|
// of the remaining header / 3
|
|
if header.FilesInfo, err = ReadFilesInfo(r, int(r.N)/3); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
case k7zEnd:
|
|
if header.MainStreamsInfo == nil {
|
|
return nil, ErrUnexpectedPropertyID
|
|
}
|
|
|
|
return header, nil
|
|
|
|
default:
|
|
return nil, ErrUnexpectedPropertyID
|
|
}
|
|
}
|
|
}
|