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) }