1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
package rpm
import (
"encoding/binary"
"io"
"io/ioutil"
)
const r_MaxHeaderSize = 33554432
// A Header stores metadata about an rpm package.
type Header struct {
Version int
Tags map[int]*Tag
}
// GetTag returns the tag with the given identifier.
//
// Nil is returned if the specified tag does not exist or the header is nil.
func (c *Header) GetTag(id int) *Tag {
if c == nil || len(c.Tags) == 0 {
return nil
}
return c.Tags[id]
}
type rpmHeader [16]byte
func (b rpmHeader) Magic() []byte { return b[:3] }
func (b rpmHeader) Version() int { return int(b[3]) }
func (b rpmHeader) IndexCount() int { return int(binary.BigEndian.Uint32(b[8:12])) }
func (b rpmHeader) Size() int { return int(binary.BigEndian.Uint32(b[12:16])) }
type rpmIndex [16]byte
func (b rpmIndex) Tag() int { return int(binary.BigEndian.Uint32(b[:4])) }
func (b rpmIndex) Type() TagType { return TagType(binary.BigEndian.Uint32(b[4:8])) }
func (b rpmIndex) Offset() int { return int(binary.BigEndian.Uint32(b[8:12])) }
func (b rpmIndex) ValueCount() int { return int(binary.BigEndian.Uint32(b[12:16])) }
// readHeader reads an RPM package file header structure from r.
func readHeader(r io.Reader, pad bool) (*Header, error) {
// decode the header structure header
var hdrBytes rpmHeader
if _, err := r.Read(hdrBytes[:]); err != nil {
return nil, err
}
if hdrBytes.Size() > r_MaxHeaderSize {
return nil, errorf(
"header size exceeds the maximum of %d: %d",
r_MaxHeaderSize,
hdrBytes.Size(),
)
}
if hdrBytes.IndexCount()*len(hdrBytes) > r_MaxHeaderSize {
return nil, errorf(
"header index size exceeds the maximum of %d: %d",
r_MaxHeaderSize,
hdrBytes.Size(),
)
}
// decode the index
indexBytes := make([]rpmIndex, hdrBytes.IndexCount())
for i := 0; i < len(indexBytes); i++ {
if _, err := r.Read(indexBytes[i][:]); err != nil {
return nil, err
}
if indexBytes[i].Offset() >= hdrBytes.Size() {
return nil, errorf(
"offset of index %d is out of range: %s",
i,
indexBytes[i].Offset(),
)
}
}
// decode the store
tags := make(map[int]*Tag, len(indexBytes))
buf := make([]byte, hdrBytes.Size())
if _, err := io.ReadFull(r, buf); err != nil {
return nil, err
}
for i, ix := range indexBytes {
if ix.ValueCount() < 1 {
return nil, errorf("invalid value count for index %d: %d", i, ix.ValueCount())
}
o := ix.Offset()
var v interface{}
switch ix.Type() {
case TagTypeBinary, TagTypeChar, TagTypeInt8:
if o+ix.ValueCount() > len(buf) {
switch ix.Type() {
case TagTypeBinary:
return nil, errorf("binary value for index %d is out of range", i+1)
case TagTypeChar:
return nil, errorf("uint8 value for index %d is out of range", i+1)
case TagTypeInt8:
return nil, errorf("int8 value for index %d is out of range", i+1)
}
return nil, errorf("value for index %d is out of range", i+1)
}
a := make([]byte, ix.ValueCount())
copy(a, buf[o:o+ix.ValueCount()])
v = a
case TagTypeInt16:
a := make([]int64, ix.ValueCount())
for v := 0; v < ix.ValueCount(); v++ {
if o+2 > len(buf) {
return nil, errorf("int16 value for index %d is out of range", i+1)
}
a[v] = int64(binary.BigEndian.Uint16(buf[o : o+2]))
o += 2
}
v = a
case TagTypeInt32:
a := make([]int64, ix.ValueCount())
for v := 0; v < ix.ValueCount(); v++ {
if o+4 > len(buf) {
return nil, errorf("int32 value for index %d is out of range", i+1)
}
a[v] = int64(binary.BigEndian.Uint32(buf[o : o+4]))
o += 4
}
v = a
case TagTypeInt64:
a := make([]int64, ix.ValueCount())
for v := 0; v < ix.ValueCount(); v++ {
if o+8 > len(buf) {
// TODO: better errors
return nil, errorf("int64 value for index %d is out of range", i+1)
}
a[v] = int64(binary.BigEndian.Uint64(buf[o : o+8]))
o += 8
}
v = a
case TagTypeString, TagTypeStringArray, TagTypeI18NString:
// allow at least one byte per string
if o+ix.ValueCount() > len(buf) {
return nil, errorf("[]string value for index %d is out of range", i+1)
}
a := make([]string, ix.ValueCount())
for s := 0; s < ix.ValueCount(); s++ {
// calculate string length
var j int
for j = 0; (o+j) < len(buf) && buf[o+j] != 0; j++ {
}
if j == len(buf) {
return nil, errorf("string value for index %d is out of range", i+1)
}
a[s] = string(buf[o : o+j])
o += j + 1
}
v = a
case TagTypeNull:
// nothing to do here
default:
// unknown data type
return nil, errorf("unknown index data type: %0X", ix.Type())
}
tags[ix.Tag()] = &Tag{
ID: ix.Tag(),
Type: ix.Type(),
Value: v,
}
}
// pad to next header
padding := int64(8-(hdrBytes.Size()%8)) % 8
if pad && padding != 0 {
if _, err := io.CopyN(ioutil.Discard, r, padding); err != nil {
return nil, err
}
}
return &Header{
Version: hdrBytes.Version(),
Tags: tags,
}, nil
}
|