Files
repokit/rpm-loader.go

402 lines
12 KiB
Go

package repokit
import "errors"
import "fmt"
import "github.com/sassoftware/go-rpmutils"
import "github.com/sassoftware/go-rpmutils/cpio"
import "os"
import "strconv"
import "strings"
var rpm_dep_items []RpmDepItem = []RpmDepItem{
{RPM_DEP_PROVIDES, rpmutils.PROVIDENAME, rpmutils.PROVIDEFLAGS, rpmutils.PROVIDEVERSION},
{RPM_DEP_CONFLICTS, rpmutils.CONFLICTNAME, rpmutils.CONFLICTFLAGS, rpmutils.CONFLICTVERSION},
{RPM_DEP_OBSOLETES, rpmutils.OBSOLETENAME, rpmutils.OBSOLETEFLAGS, rpmutils.OBSOLETEVERSION},
{RPM_DEP_REQUIRES, rpmutils.REQUIRENAME, rpmutils.REQUIREFLAGS, rpmutils.REQUIREVERSION},
{RPM_DEP_SUGGESTS, rpmutils.SUGGESTNAME, rpmutils.SUGGESTFLAGS, rpmutils.SUGGESTVERSION},
{RPM_DEP_ENHANCES, rpmutils.ENHANCENAME, rpmutils.ENHANCEFLAGS, rpmutils.ENHANCEVERSION},
{RPM_DEP_RECOMMENDS, rpmutils.RECOMMENDNAME, rpmutils.RECOMMENDFLAGS, rpmutils.RECOMMENDVERSION},
{RPM_DEP_SUPPLEMENTS, rpmutils.SUPPLEMENTNAME, rpmutils.SUPPLEMENTFLAGS, rpmutils.SUPPLEMENTVERSION},
{RPM_DEP_OLDSUGGESTS, rpmutils.OLDSUGGESTSNAME, rpmutils.OLDSUGGESTSFLAGS, rpmutils.OLDSUGGESTSVERSION},
{RPM_DEP_OLDENHANCES, rpmutils.OLDENHANCESNAME, rpmutils.OLDENHANCESFLAGS, rpmutils.OLDENHANCESVERSION},
{RPM_DEP_SENTINEL, 0, 0, 0},
}
func rpm_load_rpm(full_path string, checksum_type RpmChecksumType, location_href string, location_base string, change_log_limit int) (*RpmPackage, error) {
var pkg *RpmPackage
var err error
var stat_mod_time int64
var stat_size int64
pkg, err = RpmPackageFromRpmBase(full_path, change_log_limit)
if err != nil {
return nil, fmt.Errorf("can not get rpm header: %v", err)
}
pkg.LocationHref = location_href
pkg.LocationBase = location_base
stat_mod_time, stat_size, err = rpm_stat_file(full_path)
if err != nil {
return nil, fmt.Errorf("can not stat file: %v", err)
}
pkg.TimeFile = stat_mod_time
pkg.SizePackage = stat_size
pkg.PkgId, err = rpm_get_checksum(full_path, checksum_type, "")
if err != nil {
return nil, fmt.Errorf("error while checksum calculation: %v", err)
}
// since we did calcualte the checksum with our own type,
// we need to override the chekcsum type name
pkg.RpmChecksumType = RpmChecksumName(checksum_type)
return pkg, nil
}
func rpm_get_checksum(file_path string, checksum_type RpmChecksumType, cache_dir string) (string, error) {
var checksum string
var err error
if cache_dir != "" {
return "", fmt.Errorf("cache_dir is not supported")
}
checksum, err = rpm_checksum_file(file_path, checksum_type)
if err != nil {
return "", err
}
return checksum, nil
}
func RpmPackageFromRpmBase(full_path string, change_log_limit int) (*RpmPackage, error) {
var f *os.File
var err error
var rpm *rpmutils.Rpm
var pkg *RpmPackage
var header_range rpmutils.HeaderRange
var epoch_num uint64
var time_build uint64
var size_installed int64
var fda uint64
var size_archive int64
var file_info_array []rpmutils.FileInfo
var filenames_hashtable map[string]string
var file_info rpmutils.FileInfo
var pf RpmPackageFile
var provided_hashtable map[string]string
var ap_hashtable map[string]RpmApValueStruct
var dep_type RpmDepType
var libc_require_highest *RpmDependency
var name_tag int
var flags_tag int
var version_tag int
var filenames []string
var file_flags []uint64
var file_versions []string
var filename_size int
var file_flags_size int
var file_version_size int
var i int
var j int
var pre bool
var filename string
var num_flags uint64
var flag_str string
var full_version string
var depnfv string
var ok bool
var ap_value RpmApValueStruct
var cr_evr *RpmEVR
var dependency RpmDependency
var change_log_times []uint64
var change_log_names []string
var change_log_texts []string
var log_times_size int
var log_names_size int
var log_texts_size int
var last_time int64
var time_value int64
var changelog RpmChangelogEntry
f, err = os.Open(full_path)
if err != nil {
return nil, err
}
defer f.Close()
rpm, err = rpmutils.ReadRpm(f)
if err != nil {
return nil, err
}
pkg = &RpmPackage{}
header_range = rpm.Header.GetRange()
pkg.RpmHeaderStart = header_range.Start
pkg.RpmHeaderEnd = header_range.End
pkg.Name, err = rpm.Header.GetString(rpmutils.NAME)
if err != nil {
return nil, err
}
if rpm.Header.IsSource() {
pkg.Arch = "src"
} else {
pkg.Arch, err = rpm.Header.GetString(rpmutils.ARCH)
if err != nil {
return nil, err
}
}
pkg.Version, err = rpm.Header.GetString(rpmutils.VERSION)
if err != nil {
return nil, err
}
pkg.Release, err = rpm.Header.GetString(rpmutils.RELEASE)
if err != nil {
return nil, err
}
epoch_num, err = rpm.Header.GetUint64(rpmutils.EPOCH)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.Epoch = strconv.FormatUint(epoch_num, 10)
pkg.Summary, err = rpm.Header.GetString(rpmutils.SUMMARY)
if err != nil {
return nil, err
}
pkg.Description, err = rpm.Header.GetString(rpmutils.DESCRIPTION)
if err != nil {
return nil, err
}
pkg.Url, err = rpm.Header.GetString(rpmutils.URL)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
time_build, err = rpm.Header.GetUint64(rpmutils.BUILDTIME)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.TimeBuild = int64(time_build)
pkg.RpmLicense, err = rpm.Header.GetString(rpmutils.LICENSE)
if err != nil {
return nil, err
}
pkg.RpmVendor, err = rpm.Header.GetString(rpmutils.VENDOR)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.RpmGroup, err = rpm.Header.GetString(rpmutils.GROUP)
if err != nil {
return nil, err
}
pkg.RpmBuildHost, err = rpm.Header.GetString(rpmutils.BUILDHOST)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.RpmSourceRpm, err = rpm.Header.GetString(rpmutils.SOURCERPM)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.RpmPackager, err = rpm.Header.GetString(rpmutils.PACKAGER)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
size_installed, err = rpm.Header.InstalledSize()
if err != nil {
return nil, err
}
pkg.SizeInstalled = size_installed
fda, err = rpm.Header.GetUint64(rpmutils.FILEDIGESTALGO)
if err != nil {
// default to md5. i assume the rpm file uses md5 when this field is missing
fda = rpmutils.PGPHASHALGO_MD5
}
pkg.RpmChecksumType = rpmutils.GetFileAlgoName(int(fda))
size_archive, err = rpm.Header.PayloadSize()
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
pkg.SizeArchive = size_archive
file_info_array, err = rpm.Header.GetFiles()
if err != nil {
return nil, err
}
filenames_hashtable = make(map[string]string)
for _, file_info = range file_info_array {
pf = RpmPackageFile{}
pf.FullPath = file_info.Name()
pf.Digest = file_info.Digest()
if file_info.Mode() & ^07777 == cpio.S_ISDIR {
pf.Type = "dir"
} else if file_info.Flags() & rpmutils.RPMFILE_GHOST != 0 {
pf.Type = "ghost"
} else {
pf.Type = ""
}
filenames_hashtable[file_info.Name()] = file_info.Name()
pkg.Files = append(pkg.Files, pf)
}
provided_hashtable = make(map[string]string)
ap_hashtable = make(map[string]RpmApValueStruct)
for dep_type = RPM_DEP_PROVIDES; rpm_dep_items[dep_type].Type != RPM_DEP_SENTINEL; dep_type++ {
libc_require_highest = nil
name_tag = rpm_dep_items[dep_type].NameTag
flags_tag = rpm_dep_items[dep_type].FlagsTag
version_tag = rpm_dep_items[dep_type].VersionTag
if !rpm.Header.HasTag(name_tag) || !rpm.Header.HasTag(flags_tag) || !rpm.Header.HasTag(version_tag) {
continue
}
filenames, err = rpm.Header.GetStrings(name_tag)
if err != nil {
return nil, err
}
file_flags, err = rpm.Header.GetUint64s(flags_tag)
if err != nil {
return nil, err
}
file_versions, err = rpm.Header.GetStrings(version_tag)
if err != nil {
return nil, err
}
filename_size = len(filenames)
file_flags_size = len(file_flags)
file_version_size = len(file_versions)
for i = 0; i < file_flags_size && i < filename_size && i < file_version_size; i++ {
pre = false
filename = filenames[i]
num_flags = file_flags[i]
flag_str = rpm_flag_to_str(num_flags)
full_version = file_versions[i]
depnfv = filename + flag_str + full_version
if dep_type == RPM_DEP_REQUIRES {
if strings.HasPrefix(filename, "rpmlib(") {
continue
}
if strings.HasPrefix(filename, "/") && filenames_hashtable[filename] != "" {
if rpm_is_primary_file(filename) {
continue
}
}
_, ok = provided_hashtable[depnfv]
if ok {
continue
}
if num_flags & (rpmutils.RPMSENSE_PREREQ | rpmutils.RPMSENSE_SCRIPT_PRE | rpmutils.RPMSENSE_POSTTRANS | rpmutils.RPMSENSE_PRETRANS | rpmutils.RPMSENSE_SCRIPT_POST) != 0 {
pre = true
}
ap_value, ok = ap_hashtable[filename]
if ok {
if ap_value.Flags == flag_str && ap_value.Version == full_version && ap_value.Pre == pre {
continue
}
}
}
cr_evr = rpm_str_to_evr(full_version)
if full_version != "" && cr_evr.Epoch == "" {
continue
}
dependency = RpmDependency{Name: filename, Flags: flag_str, Epoch: cr_evr.Epoch, Version: cr_evr.Version, Release: cr_evr.Release}
switch dep_type {
case RPM_DEP_PROVIDES:
provided_hashtable[depnfv] = ""
pkg.Provides = append(pkg.Provides, dependency)
case RPM_DEP_CONFLICTS:
pkg.Conflicts = append(pkg.Conflicts, dependency)
case RPM_DEP_OBSOLETES:
pkg.Obsoletes = append(pkg.Obsoletes, dependency)
case RPM_DEP_REQUIRES:
if num_flags & RPM_SENSE_MISSINGOK != 0 {
pkg.Recommends = append(pkg.Recommends, dependency)
break
}
dependency.Pre = pre
if strings.HasPrefix(dependency.Name, "libc.so.6") {
if libc_require_highest == nil {
libc_require_highest = &dependency
} else if rpm_compare_dependency(libc_require_highest.Name, dependency.Name) == -1 {
libc_require_highest = &dependency
}
break
}
pkg.Requires = append(pkg.Requires, dependency)
ap_hashtable[dependency.Name] = RpmApValueStruct{Flags: flag_str, Version: full_version, Pre: dependency.Pre}
case RPM_DEP_SUGGESTS:
pkg.Suggests = append(pkg.Suggests, dependency)
case RPM_DEP_ENHANCES:
pkg.Enhances = append(pkg.Enhances, dependency)
case RPM_DEP_RECOMMENDS:
pkg.Recommends = append(pkg.Recommends, dependency)
case RPM_DEP_SUPPLEMENTS:
pkg.Supplements = append(pkg.Supplements, dependency)
case RPM_DEP_OLDSUGGESTS:
if num_flags & RPM_SENSE_STRONG != 0 {
pkg.Recommends = append(pkg.Recommends, dependency)
} else {
pkg.Suggests = append(pkg.Suggests, dependency)
}
case RPM_DEP_OLDENHANCES:
if num_flags & RPM_SENSE_STRONG != 0 {
pkg.Supplements = append(pkg.Supplements, dependency)
} else {
pkg.Enhances = append(pkg.Enhances, dependency)
}
default:
}
}
if dep_type == RPM_DEP_REQUIRES && libc_require_highest != nil {
pkg.Requires = append(pkg.Requires, *libc_require_highest)
}
}
change_log_times, err = rpm.Header.GetUint64s(rpmutils.CHANGELOGTIME)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
change_log_names, err = rpm.Header.GetStrings(rpmutils.CHANGELOGNAME)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
change_log_texts, err = rpm.Header.GetStrings(rpmutils.CHANGELOGTEXT)
if !rpm_value_exists_or_no_such_tag(err) {
return nil, err
}
log_times_size = len(change_log_times)
log_names_size = len(change_log_names)
log_texts_size = len(change_log_texts)
last_time = 0
for i = 0; i < log_times_size && i < log_names_size && i < log_texts_size && change_log_limit > 0; i++ {
time_value = int64(change_log_times[i])
changelog = RpmChangelogEntry{Author: strings.TrimSpace(change_log_names[i]), Date: time_value, Changelog: change_log_texts[i]}
pkg.Changelogs = append(pkg.Changelogs, changelog)
change_log_limit--
if last_time == time_value {
for j = len(pkg.Changelogs) - 2; j >= 0 && pkg.Changelogs[j].Date == time_value; j-- {
pkg.Changelogs[j].Date--
time_value--
}
} else {
last_time = time_value
}
}
rpm_reverse_array(pkg.Changelogs)
return pkg, nil
}
func rpm_value_exists_or_no_such_tag(err error) bool {
var target_err rpmutils.NoSuchTagError
return err == nil || errors.As(err, &target_err)
}