mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-22 18:58:29 +00:00
380 lines
9.9 KiB
Go
380 lines
9.9 KiB
Go
package rpm
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
// A Package is an rpm package file.
|
|
type Package struct {
|
|
Lead Lead
|
|
Signature Header
|
|
Header Header
|
|
}
|
|
|
|
var _ Version = &Package{}
|
|
|
|
// Read reads an rpm package from r.
|
|
//
|
|
// When this function returns, the reader will be positioned at the start of the
|
|
// package payload. Use Package.PayloadFormat and Package.PayloadCompression to
|
|
// determine how to decompress and unarchive the payload.
|
|
func Read(r io.Reader) (*Package, error) {
|
|
lead, err := readLead(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig, err := readHeader(r, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hdr, err := readHeader(r, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Package{
|
|
Lead: *lead,
|
|
Signature: *sig,
|
|
Header: *hdr,
|
|
}, nil
|
|
}
|
|
|
|
// Open opens an rpm package from the file system.
|
|
//
|
|
// Once the package headers are read, the underlying reader is closed and cannot
|
|
// be used to read the package payload. To read the package payload, open the
|
|
// package with os.Open and read the headers with Read. You may then use the
|
|
// same reader to read the payload.
|
|
func Open(name string) (*Package, error) {
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return Read(bufio.NewReader(f))
|
|
}
|
|
|
|
// dependencies translates the given tag values into a slice of package
|
|
// relationships such as provides, conflicts, obsoletes and requires.
|
|
func (c *Package) dependencies(nevrsTagID, flagsTagID, namesTagID, versionsTagID int) []Dependency {
|
|
// TODO: Implement NEVRS tags
|
|
// TODO: error handling
|
|
flgs := c.Header.GetTag(flagsTagID).Int64Slice()
|
|
names := c.Header.GetTag(namesTagID).StringSlice()
|
|
vers := c.Header.GetTag(versionsTagID).StringSlice()
|
|
deps := make([]Dependency, len(names))
|
|
for i := 0; i < len(names); i++ {
|
|
deps[i] = &dependency{
|
|
flags: int(flgs[i]),
|
|
name: names[i],
|
|
version: vers[i],
|
|
}
|
|
}
|
|
return deps
|
|
}
|
|
|
|
// String returns the package identifier in the form
|
|
// '[name]-[version]-[release].[architecture]'.
|
|
func (c *Package) String() string {
|
|
return fmt.Sprintf("%s-%s-%s.%s", c.Name(), c.Version(), c.Release(), c.Architecture())
|
|
}
|
|
|
|
func (c *Package) GPGSignature() GPGSignature {
|
|
return c.Signature.GetTag(1002).Bytes()
|
|
}
|
|
|
|
// For tag definitions, see:
|
|
// https://github.com/rpm-software-management/rpm/blob/master/lib/rpmtag.h#L61
|
|
|
|
func (c *Package) Name() string {
|
|
return c.Header.GetTag(1000).String()
|
|
}
|
|
|
|
func (c *Package) Version() string {
|
|
return c.Header.GetTag(1001).String()
|
|
}
|
|
|
|
func (c *Package) Release() string {
|
|
return c.Header.GetTag(1002).String()
|
|
}
|
|
|
|
func (c *Package) Epoch() int {
|
|
return int(c.Header.GetTag(1003).Int64())
|
|
}
|
|
|
|
func (c *Package) Requires() []Dependency {
|
|
return c.dependencies(5041, 1048, 1049, 1050)
|
|
}
|
|
|
|
func (c *Package) Provides() []Dependency {
|
|
return c.dependencies(5042, 1112, 1047, 1113)
|
|
}
|
|
|
|
func (c *Package) Conflicts() []Dependency {
|
|
return c.dependencies(5044, 1053, 1054, 1055)
|
|
}
|
|
|
|
func (c *Package) Obsoletes() []Dependency {
|
|
return c.dependencies(5043, 1114, 1090, 1115)
|
|
}
|
|
|
|
func (c *Package) Suggests() []Dependency {
|
|
return c.dependencies(5059, 5051, 5049, 5050)
|
|
}
|
|
|
|
func (c *Package) Enhances() []Dependency {
|
|
return c.dependencies(5061, 5057, 5055, 5056)
|
|
}
|
|
|
|
func (c *Package) Recommends() []Dependency {
|
|
return c.dependencies(5058, 5048, 5046, 5047)
|
|
}
|
|
|
|
func (c *Package) Supplements() []Dependency {
|
|
return c.dependencies(5060, 5051, 5052, 5053)
|
|
}
|
|
|
|
// Files returns file information for each file that is installed by this RPM
|
|
// package.
|
|
func (c *Package) Files() []FileInfo {
|
|
ixs := c.Header.GetTag(1116).Int64Slice()
|
|
names := c.Header.GetTag(1117).StringSlice()
|
|
dirs := c.Header.GetTag(1118).StringSlice()
|
|
modes := c.Header.GetTag(1030).Int64Slice()
|
|
sizes := c.Header.GetTag(1028).Int64Slice()
|
|
times := c.Header.GetTag(1034).Int64Slice()
|
|
flags := c.Header.GetTag(1037).Int64Slice()
|
|
owners := c.Header.GetTag(1039).StringSlice()
|
|
groups := c.Header.GetTag(1040).StringSlice()
|
|
digests := c.Header.GetTag(1035).StringSlice()
|
|
linknames := c.Header.GetTag(1036).StringSlice()
|
|
a := make([]FileInfo, len(names))
|
|
for i := 0; i < len(names); i++ {
|
|
a[i] = FileInfo{
|
|
name: dirs[ixs[i]] + names[i],
|
|
mode: fileModeFromInt64(modes[i]),
|
|
size: sizes[i],
|
|
modTime: time.Unix(times[i], 0),
|
|
flags: flags[i],
|
|
owner: owners[i],
|
|
group: groups[i],
|
|
digest: digests[i],
|
|
linkname: linknames[i],
|
|
}
|
|
}
|
|
return a
|
|
}
|
|
|
|
// fileModeFromInt64 converts the 16 bit value returned from a typical
|
|
// unix/linux stat call to the bitmask that go uses to produce an os
|
|
// neutral representation. It is incorrect to just cast the 16 bit
|
|
// value directly to a os.FileMode. The result of stat is 4 bits to
|
|
// specify the type of the object, this is a value in the range 0 to
|
|
// 15, rather than a bitfield, 3 bits to note suid, sgid and sticky,
|
|
// and 3 sets of 3 bits for rwx permissions for user, group and other.
|
|
// An os.FileMode has the same 9 bits for permissions, but rather than
|
|
// using an enum for the type it has individual bits. As a concrete
|
|
// example, a block device has the 1<<26 bit set (os.ModeDevice) in
|
|
// the os.FileMode, but has type 0x6000 (syscall.S_IFBLK). A regular
|
|
// file is represented in os.FileMode by not having any of the bits in
|
|
// os.ModeType set (i.e. is not a directory, is not a symlink, is not
|
|
// a named pipe...) whilst a regular file has value syscall.S_IFREG
|
|
// (0x8000) in the mode field from stat.
|
|
func fileModeFromInt64(mode int64) os.FileMode {
|
|
fm := os.FileMode(mode & 0777)
|
|
switch mode & syscall.S_IFMT {
|
|
case syscall.S_IFBLK:
|
|
fm |= os.ModeDevice
|
|
case syscall.S_IFCHR:
|
|
fm |= os.ModeDevice | os.ModeCharDevice
|
|
case syscall.S_IFDIR:
|
|
fm |= os.ModeDir
|
|
case syscall.S_IFIFO:
|
|
fm |= os.ModeNamedPipe
|
|
case syscall.S_IFLNK:
|
|
fm |= os.ModeSymlink
|
|
case syscall.S_IFREG:
|
|
// nothing to do
|
|
case syscall.S_IFSOCK:
|
|
fm |= os.ModeSocket
|
|
}
|
|
if mode&syscall.S_ISGID != 0 {
|
|
fm |= os.ModeSetgid
|
|
}
|
|
if mode&syscall.S_ISUID != 0 {
|
|
fm |= os.ModeSetuid
|
|
}
|
|
if mode&syscall.S_ISVTX != 0 {
|
|
fm |= os.ModeSticky
|
|
}
|
|
return fm
|
|
}
|
|
|
|
func (c *Package) Summary() string {
|
|
return strings.Join(c.Header.GetTag(1004).StringSlice(), "\n")
|
|
}
|
|
|
|
func (c *Package) Description() string {
|
|
return strings.Join(c.Header.GetTag(1005).StringSlice(), "\n")
|
|
}
|
|
|
|
func (c *Package) BuildTime() time.Time {
|
|
return time.Unix(c.Header.GetTag(1006).Int64(), 0)
|
|
}
|
|
|
|
func (c *Package) BuildHost() string {
|
|
return c.Header.GetTag(1007).String()
|
|
}
|
|
|
|
func (c *Package) InstallTime() time.Time {
|
|
return time.Unix(c.Header.GetTag(1008).Int64(), 0)
|
|
}
|
|
|
|
// Size specifies the disk space consumed by installation of the package.
|
|
func (c *Package) Size() uint64 {
|
|
return uint64(c.Header.GetTag(1009).Int64())
|
|
}
|
|
|
|
// ArchiveSize specifies the size of the archived payload of the package in
|
|
// bytes.
|
|
func (c *Package) ArchiveSize() uint64 {
|
|
if i := uint64(c.Signature.GetTag(1007).Int64()); i > 0 {
|
|
return i
|
|
}
|
|
|
|
return uint64(c.Header.GetTag(1046).Int64())
|
|
}
|
|
|
|
func (c *Package) Distribution() string {
|
|
return c.Header.GetTag(1010).String()
|
|
}
|
|
|
|
func (c *Package) Vendor() string {
|
|
return c.Header.GetTag(1011).String()
|
|
}
|
|
|
|
func (c *Package) GIFImage() []byte {
|
|
return c.Header.GetTag(1012).Bytes()
|
|
}
|
|
|
|
func (c *Package) XPMImage() []byte {
|
|
return c.Header.GetTag(1013).Bytes()
|
|
}
|
|
|
|
func (c *Package) License() string {
|
|
return c.Header.GetTag(1014).String()
|
|
}
|
|
|
|
func (c *Package) Packager() string {
|
|
return c.Header.GetTag(1015).String()
|
|
}
|
|
|
|
func (c *Package) Groups() []string {
|
|
return c.Header.GetTag(1016).StringSlice()
|
|
}
|
|
|
|
func (c *Package) ChangeLog() []string {
|
|
return c.Header.GetTag(1017).StringSlice()
|
|
}
|
|
|
|
func (c *Package) Source() []string {
|
|
return c.Header.GetTag(1018).StringSlice()
|
|
}
|
|
|
|
func (c *Package) Patch() []string {
|
|
return c.Header.GetTag(1019).StringSlice()
|
|
}
|
|
|
|
func (c *Package) URL() string {
|
|
return c.Header.GetTag(1020).String()
|
|
}
|
|
|
|
func (c *Package) OperatingSystem() string {
|
|
return c.Header.GetTag(1021).String()
|
|
}
|
|
|
|
func (c *Package) Architecture() string {
|
|
return c.Header.GetTag(1022).String()
|
|
}
|
|
|
|
func (c *Package) PreInstallScript() string {
|
|
return c.Header.GetTag(1023).String()
|
|
}
|
|
|
|
func (c *Package) PostInstallScript() string {
|
|
return c.Header.GetTag(1024).String()
|
|
}
|
|
|
|
func (c *Package) PreUninstallScript() string {
|
|
return c.Header.GetTag(1025).String()
|
|
}
|
|
|
|
func (c *Package) PostUninstallScript() string {
|
|
return c.Header.GetTag(1026).String()
|
|
}
|
|
|
|
func (c *Package) OldFilenames() []string {
|
|
return c.Header.GetTag(1027).StringSlice()
|
|
}
|
|
|
|
func (c *Package) Icon() []byte {
|
|
return c.Header.GetTag(1043).Bytes()
|
|
}
|
|
|
|
func (c *Package) SourceRPM() string {
|
|
return c.Header.GetTag(1044).String()
|
|
}
|
|
|
|
func (c *Package) RPMVersion() string {
|
|
return c.Header.GetTag(1064).String()
|
|
}
|
|
|
|
func (c *Package) Platform() string {
|
|
return c.Header.GetTag(1132).String()
|
|
}
|
|
|
|
// PayloadFormat returns the name of the format used for the package payload.
|
|
// Typically cpio.
|
|
func (c *Package) PayloadFormat() string {
|
|
return c.Header.GetTag(1124).String()
|
|
}
|
|
|
|
// PayloadCompression returns the name of the compression used for the package
|
|
// payload. Typically xz.
|
|
func (c *Package) PayloadCompression() string {
|
|
return c.Header.GetTag(1125).String()
|
|
}
|
|
|
|
// Sort sorts a slice of packages lexically by name ascending and then by
|
|
// version descending. Version is evaluated first by epoch, then by version
|
|
// string, then by release.
|
|
func Sort(x []*Package) { sort.Sort(PackageSlice(x)) }
|
|
|
|
// PackageSlice implements sort.Interface for a slice of packages. Packages are
|
|
// sorted lexically by name ascending and then by version descending. Version is
|
|
// evaluated first by epoch, then by version string, then by release.
|
|
type PackageSlice []*Package
|
|
|
|
// Sort is a convenience method: x.Sort() calls sort.Sort(x).
|
|
func (x PackageSlice) Sort() { sort.Sort(x) }
|
|
|
|
func (x PackageSlice) Len() int { return len(x) }
|
|
|
|
func (x PackageSlice) Less(i, j int) bool {
|
|
a, b := x[i].Name(), x[j].Name()
|
|
if a == b {
|
|
return Compare(x[i], x[j]) == 1
|
|
}
|
|
return a < b
|
|
}
|
|
|
|
func (x PackageSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
var _ sort.Interface = PackageSlice{}
|