402 lines
12 KiB
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)
|
|
}
|