peridot/peridot/composetools/rpm.go

303 lines
9.3 KiB
Go

// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
package composetools
import (
"errors"
"fmt"
"github.com/gobwas/glob"
"path/filepath"
"peridot.resf.org/apollo/rpmutils"
"peridot.resf.org/peridot/yummeta"
"peridot.resf.org/utils"
"strings"
)
var (
ErrInvalidNVR = errors.New("invalid NVR")
)
var (
debugSuffixes = []string{"-debuginfo", "-debugsource", "-debuginfo-common"}
soGlob = glob.MustCompile("*.so.*")
gtk2EnginesGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/engines")
gtk2ModulesGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/modules")
gtk2ImmodulesGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/immodules")
gtk2LoadersGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/loaders")
gtk2PrintBackendsGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/printbackends")
gtk2FilesystemsGlob = glob.MustCompile("/usr/lib*/gtk-2.0/*/filesystems")
qtPluginsGlob = glob.MustCompile("/usr/lib*/qt*/plugins/*")
kdePluginsGlob = glob.MustCompile("/usr/lib*/kde*/plugins/*")
qt5QmlGlob = glob.MustCompile("/usr/lib*/qt5/qml/*")
gdkPixbufLoadersGlob = glob.MustCompile("/usr/lib*/gdk-pixbuf-2.0/*/loaders")
xinePluginsGlob = glob.MustCompile("/usr/lib*/xine/plugins/*")
)
func StripDebugSuffixes(name string) string {
ret := name
for _, suffix := range debugSuffixes {
ret = strings.TrimSuffix(ret, suffix)
}
return ret
}
func IsDebugPackage(name string) bool {
for _, suffix := range debugSuffixes {
if strings.HasSuffix(name, suffix) {
return true
}
}
return false
}
func IsDebugPackageNvra(nvra string) (bool, error) {
if !rpmutils.NVR().MatchString(nvra) {
return false, ErrInvalidNVR
}
if rpmutils.NVRUnusualRelease().MatchString(nvra) {
match := rpmutils.NVRUnusualRelease().FindStringSubmatch(nvra)
return IsDebugPackage(match[1]), nil
}
match := rpmutils.NVR().FindStringSubmatch(nvra)
return IsDebugPackage(match[1]), nil
}
func GenNevraPrimaryPkg(pkg *yummeta.PrimaryPackage) string {
return fmt.Sprintf("%s-%s:%s-%s.%s", pkg.Name, pkg.Version.Epoch, pkg.Version.Ver, pkg.Version.Rel, pkg.Arch)
}
func MultilibMethod(pkg *yummeta.PrimaryPackage, pkgFiles []*yummeta.FilelistsFile, excludeFilter []string, includeList []string) bool {
prefer64 := []string{"gdb", "frysk", "systemtap", "systemtap-runtime", "ltrace", "strace"}
if strings.Contains(pkg.Arch, "64") {
if utils.StrContains(pkg.Name, prefer64) {
return true
}
if strings.HasPrefix(pkg.Name, "kernel") {
for _, prov := range pkg.Format.RpmProvides.RpmEntries {
if prov.Name == "kernel" || prov.Name == "kernel-devel" {
return true
}
}
}
}
return false
}
// RuntimeMultilib returns if a given package should be multilib tested by runtime rules (contains *.so)
// Ported from https://pagure.io/releng/python-multilib/blob/master/f/multilib/multilib.py#_117
func RuntimeMultilib(pkg *yummeta.PrimaryPackage, pkgFiles []*yummeta.FilelistsFile, excludeFilter []string, includeList []string) (bool, error) {
rootLibDirs := []string{"/lib", "/lib64"}
usrLibDirs := []string{"/usr/lib", "/usr/lib64"}
libDirs := append(rootLibDirs, usrLibDirs...)
oprofileLibDirs := []string{"/usr/lib/oprofile", "/usr/lib64/oprofile"}
wineDirs := []string{"/usr/lib/wine", "/usr/lib64/wine"}
saneDirs := []string{"/usr/lib/sane", "/usr/lib64/sane"}
byDir := []string{"/etc/lsb-release.d"}
extraPkgs := []string{"alsa-lib", "dri", "gtk-2.0/modules", "gtk-2.0/immodules", "krb5/plugins", "sasl2", "vdpau"}
for _, p := range extraPkgs {
byDir = append(byDir, filepath.Join("/usr/lib/", p))
byDir = append(byDir, filepath.Join("/usr/lib64/", p))
}
for _, p := range rootLibDirs {
byDir = append(byDir, filepath.Join(p, "security"))
}
// If excluded, return false
for _, filter := range excludeFilter {
g, err := glob.Compile(filter)
if err != nil {
return false, err
}
if g.Match(pkg.Name) {
return false, nil
}
}
// If forcefully included, return true
if utils.StrContains(pkg.Name, includeList) {
return true, nil
}
if MultilibMethod(pkg, pkgFiles, excludeFilter, includeList) {
return true, nil
}
if strings.HasPrefix(pkg.Name, "kernel") {
for _, prov := range pkg.Format.RpmProvides.RpmEntries {
if prov.Name == "kernel" {
return false, nil
}
}
}
for _, file := range pkgFiles {
dirName := filepath.Dir(file.Value)
fileName := filepath.Base(file.Value)
// If *.so file in LIBDIRS, return true
if utils.StrContains(dirName, libDirs) && soGlob.Match(fileName) {
return true, nil
}
if utils.StrContains(dirName, byDir) {
return true, nil
}
if dirName == "/etc/ld.so.conf.d" && strings.HasSuffix(fileName, ".conf") {
return true, nil
}
if utils.StrContains(dirName, rootLibDirs) && (strings.HasPrefix(fileName, "libnss_") || strings.HasPrefix(fileName, "libdb-")) {
return true, nil
}
// Rest of the checks here is for usrLibDirs, skip if it doesn't start with usrLibDirs
startsWithUsrLibDirs := false
for _, usrLibDir := range usrLibDirs {
if strings.HasPrefix(dirName, usrLibDir) {
startsWithUsrLibDirs = true
break
}
}
if !startsWithUsrLibDirs {
continue
}
if strings.HasPrefix(dirName, "/usr/lib/gtk-2.0") || strings.HasPrefix(dirName, "/usr/lib64/gtk-2.0") {
if gtk2EnginesGlob.Match(dirName) {
return true, nil
}
if gtk2ModulesGlob.Match(dirName) {
return true, nil
}
if gtk2ImmodulesGlob.Match(dirName) {
return true, nil
}
if gtk2LoadersGlob.Match(dirName) {
return true, nil
}
if gtk2PrintBackendsGlob.Match(dirName) {
return true, nil
}
if gtk2FilesystemsGlob.Match(dirName) {
return true, nil
}
// If none-matches, continue
continue
}
// gstreamer
if strings.HasPrefix(dirName, "/usr/lib/gstreamer-") || strings.HasPrefix(dirName, "/usr/lib64/gstreamer-") {
return true, nil
}
// qt/kde
if qtPluginsGlob.Match(dirName) {
return true, nil
}
if kdePluginsGlob.Match(dirName) {
return true, nil
}
// qml
if qt5QmlGlob.Match(dirName) {
return true, nil
}
if gdkPixbufLoadersGlob.Match(dirName) {
return true, nil
}
if xinePluginsGlob.Match(dirName) {
return true, nil
}
if utils.StrContains(dirName, oprofileLibDirs) && soGlob.Match(fileName) {
return true, nil
}
if utils.StrContains(dirName, wineDirs) && strings.HasSuffix(fileName, ".so") {
return true, nil
}
if utils.StrContains(dirName, saneDirs) && strings.HasPrefix(fileName, "libsane-") {
return true, nil
}
}
return false, nil
}
func DevelMultilib(pkg *yummeta.PrimaryPackage, pkgFiles []*yummeta.FilelistsFile, excludeFilter []string, includeList []string) (bool, error) {
// If excluded, return false
for _, filter := range excludeFilter {
g, err := glob.Compile(filter)
if err != nil {
return false, err
}
if g.Match(pkg.Name) {
return false, nil
}
}
if utils.StrContains(pkg.Name, excludeFilter) {
return false, nil
}
// If forcefully included, return true
if utils.StrContains(pkg.Name, includeList) {
return true, nil
}
// If allowed in RuntimeMultilib, return true
runtimeOk, err := RuntimeMultilib(pkg, pkgFiles, excludeFilter, includeList)
if err != nil {
return false, nil
}
if runtimeOk {
return true, nil
}
if strings.HasPrefix(pkg.Name, "ghc-") {
return false, nil
}
if strings.HasPrefix(pkg.Name, "kernel") {
for _, prov := range pkg.Format.RpmProvides.RpmEntries {
if prov.Name == "kernel-devel" {
return false, nil
}
if strings.HasSuffix(prov.Name, "-devel") || strings.HasSuffix(prov.Name, "-static") {
return true, nil
}
}
}
if strings.HasSuffix(pkg.Name, "-devel") {
return true, nil
}
if strings.HasSuffix(pkg.Name, "-static") {
return true, nil
}
return false, nil
}