feat: allow env expansion in conflicts, suggests, recommends, depends… (#548)
* feat: allow env expansion in conflicts, suggests, recommends, depends, provides, and replaces. Ensuring empty env vars get stripped to not cause issues
* added test for boolean dependencies for rpm, deb, and apk.
* fix: fork rpmpack to make it easier to add features and adapt it
* test: fix linter issues
* test: fix ruleguard issues
2022-09-24 19:07:11 +02:00
|
|
|
// Copyright 2019 Google LLC
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package rpmpack
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
signatures = 0x3e
|
|
|
|
immutable = 0x3f
|
|
|
|
|
|
|
|
typeInt16 = 0x03
|
|
|
|
typeInt32 = 0x04
|
|
|
|
typeString = 0x06
|
|
|
|
typeBinary = 0x07
|
|
|
|
typeStringArray = 0x08
|
|
|
|
)
|
|
|
|
|
|
|
|
// Only integer types are aligned. This is not just an optimization - some versions
|
|
|
|
// of rpm fail when integers are not aligned. Other versions fail when non-integers are aligned.
|
|
|
|
var boundaries = map[int]int{
|
|
|
|
typeInt16: 2,
|
|
|
|
typeInt32: 4,
|
|
|
|
}
|
|
|
|
|
|
|
|
type IndexEntry struct {
|
|
|
|
rpmtype, count int
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e IndexEntry) indexBytes(tag, contentOffset int) []byte {
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
if err := binary.Write(b, binary.BigEndian, []int32{int32(tag), int32(e.rpmtype), int32(contentOffset), int32(e.count)}); err != nil {
|
|
|
|
// binary.Write can fail if the underlying Write fails, or the types are invalid.
|
|
|
|
// bytes.Buffer's write never error out, it can only panic with OOM.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return b.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func intEntry(rpmtype, size int, value interface{}) IndexEntry {
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
if err := binary.Write(b, binary.BigEndian, value); err != nil {
|
|
|
|
// binary.Write can fail if the underlying Write fails, or the types are invalid.
|
|
|
|
// bytes.Buffer's write never error out, it can only panic with OOM.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return IndexEntry{rpmtype, size, b.Bytes()}
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryInt16(value []int16) IndexEntry {
|
|
|
|
return intEntry(typeInt16, len(value), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryUint16(value []uint16) IndexEntry {
|
|
|
|
return intEntry(typeInt16, len(value), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryInt32(value []int32) IndexEntry {
|
|
|
|
return intEntry(typeInt32, len(value), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryUint32(value []uint32) IndexEntry {
|
|
|
|
return intEntry(typeInt32, len(value), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryString(value string) IndexEntry {
|
|
|
|
return IndexEntry{typeString, 1, append([]byte(value), byte(0o0))}
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryBytes(value []byte) IndexEntry {
|
|
|
|
return IndexEntry{typeBinary, len(value), value}
|
|
|
|
}
|
|
|
|
|
|
|
|
func EntryStringSlice(value []string) IndexEntry {
|
|
|
|
b := [][]byte{}
|
|
|
|
for _, v := range value {
|
|
|
|
b = append(b, []byte(v))
|
|
|
|
}
|
|
|
|
bb := append(bytes.Join(b, []byte{0o0}), byte(0o0))
|
|
|
|
return IndexEntry{typeStringArray, len(value), bb}
|
|
|
|
}
|
|
|
|
|
|
|
|
type index struct {
|
|
|
|
entries map[int]IndexEntry
|
|
|
|
h int
|
|
|
|
}
|
|
|
|
|
|
|
|
func newIndex(h int) *index {
|
|
|
|
return &index{entries: make(map[int]IndexEntry), h: h}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *index) Add(tag int, e IndexEntry) {
|
|
|
|
i.entries[tag] = e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *index) AddEntries(m map[int]IndexEntry) {
|
|
|
|
for t, e := range m {
|
|
|
|
i.Add(t, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *index) sortedTags() []int {
|
|
|
|
t := []int{}
|
|
|
|
for k := range i.entries {
|
|
|
|
t = append(t, k)
|
|
|
|
}
|
|
|
|
sort.Ints(t)
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func pad(w *bytes.Buffer, rpmtype, offset int) {
|
|
|
|
// We need to align integer entries...
|
|
|
|
if b, ok := boundaries[rpmtype]; ok && offset%b != 0 {
|
|
|
|
if _, err := w.Write(make([]byte, b-offset%b)); err != nil {
|
|
|
|
// binary.Write can fail if the underlying Write fails, or the types are invalid.
|
|
|
|
// bytes.Buffer's write never error out, it can only panic with OOM.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes returns the bytes of the index.
|
|
|
|
func (i *index) Bytes() ([]byte, error) {
|
|
|
|
w := &bytes.Buffer{}
|
|
|
|
// Even the header has three parts: The lead, the index entries, and the entries.
|
|
|
|
// Because of alignment, we can only tell the actual size and offset after writing
|
|
|
|
// the entries.
|
|
|
|
entryData := &bytes.Buffer{}
|
|
|
|
tags := i.sortedTags()
|
|
|
|
offsets := make([]int, len(tags))
|
|
|
|
for ii, tag := range tags {
|
|
|
|
e := i.entries[tag]
|
|
|
|
pad(entryData, e.rpmtype, entryData.Len())
|
|
|
|
offsets[ii] = entryData.Len()
|
|
|
|
entryData.Write(e.data)
|
|
|
|
}
|
|
|
|
entryData.Write(i.eigenHeader().data)
|
|
|
|
|
|
|
|
// 4 magic and 4 reserved
|
|
|
|
w.Write([]byte{0x8e, 0xad, 0xe8, 0x01, 0, 0, 0, 0})
|
|
|
|
// 4 count and 4 size
|
|
|
|
// We add the pseudo-entry "eigenHeader" to count.
|
|
|
|
if err := binary.Write(w, binary.BigEndian, []int32{int32(len(i.entries)) + 1, int32(entryData.Len())}); err != nil {
|
2023-03-04 16:54:42 +01:00
|
|
|
return nil, fmt.Errorf("failed to write eigenHeader: %w", err)
|
feat: allow env expansion in conflicts, suggests, recommends, depends… (#548)
* feat: allow env expansion in conflicts, suggests, recommends, depends, provides, and replaces. Ensuring empty env vars get stripped to not cause issues
* added test for boolean dependencies for rpm, deb, and apk.
* fix: fork rpmpack to make it easier to add features and adapt it
* test: fix linter issues
* test: fix ruleguard issues
2022-09-24 19:07:11 +02:00
|
|
|
}
|
|
|
|
// Write the eigenHeader index entry
|
|
|
|
w.Write(i.eigenHeader().indexBytes(i.h, entryData.Len()-0x10))
|
|
|
|
// Write all of the other index entries
|
|
|
|
for ii, tag := range tags {
|
|
|
|
e := i.entries[tag]
|
|
|
|
w.Write(e.indexBytes(tag, offsets[ii]))
|
|
|
|
}
|
|
|
|
w.Write(entryData.Bytes())
|
|
|
|
return w.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// the eigenHeader is a weird entry. Its index entry is sorted first, but its content
|
|
|
|
// is last. The content is a 16 byte index entry, which is almost the same as the index
|
|
|
|
// entry except for the offset. The offset here is ... minus the length of the index entry region.
|
|
|
|
// Which is always 0x10 * number of entries.
|
|
|
|
// I kid you not.
|
|
|
|
func (i *index) eigenHeader() IndexEntry {
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
if err := binary.Write(b, binary.BigEndian, []int32{int32(i.h), int32(typeBinary), -int32(0x10 * (len(i.entries) + 1)), int32(0x10)}); err != nil {
|
|
|
|
// binary.Write can fail if the underlying Write fails, or the types are invalid.
|
|
|
|
// bytes.Buffer's write never error out, it can only panic with OOM.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return EntryBytes(b.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func lead(name, fullVersion string) []byte {
|
|
|
|
// RPM format = 0xedabeedb
|
|
|
|
// version 3.0 = 0x0300
|
|
|
|
// type binary = 0x0000
|
|
|
|
// machine archnum (i386?) = 0x0001
|
|
|
|
// name ( 66 bytes, with null termination)
|
|
|
|
// osnum (linux?) = 0x0001
|
|
|
|
// sig type (header-style) = 0x0005
|
|
|
|
// reserved 16 bytes of 0x00
|
|
|
|
n := []byte(fmt.Sprintf("%s-%s", name, fullVersion))
|
|
|
|
if len(n) > 65 {
|
|
|
|
n = n[:65]
|
|
|
|
}
|
|
|
|
n = append(n, make([]byte, 66-len(n))...)
|
|
|
|
b := []byte{0xed, 0xab, 0xee, 0xdb, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}
|
|
|
|
b = append(b, n...)
|
|
|
|
b = append(b, []byte{0x00, 0x01, 0x00, 0x05}...)
|
|
|
|
b = append(b, make([]byte, 16)...)
|
|
|
|
return b
|
|
|
|
}
|