Switch to new BPM file structure #8

Merged
EnumDev merged 11 commits from improve_bpm_structure into master 2024-10-23 06:22:40 +00:00
Showing only changes of commit a054717b23 - Show all commits

View File

@ -14,6 +14,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"slices" "slices"
"sort"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@ -148,6 +149,7 @@ func ReadPackage(filename string) (*BPMPackage, error) {
var pkgFiles []*PackageFileEntry var pkgFiles []*PackageFileEntry
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
fmt.Println("a")
return nil, err return nil, err
} }
@ -468,34 +470,33 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
return strings.Join(ret, "\n") return strings.Join(ret, "\n")
} }
func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) (error, []string) { func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error {
var files []string
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
err := ExecutePackageScripts(filename, rootDir, Install, false) err := ExecutePackageScripts(filename, rootDir, Install, false)
if err != nil { if err != nil {
return err, nil return err
} }
} else { } else {
err := ExecutePackageScripts(filename, rootDir, Update, false) err := ExecutePackageScripts(filename, rootDir, Update, false)
if err != nil { if err != nil {
return err, nil return err
} }
} }
seenHardlinks := make(map[string]string) seenHardlinks := make(map[string]string)
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
return err, nil return err
} }
tarballFile, err := ReadTarballContent(filename, "files.tar.gz") tarballFile, err := ReadTarballContent(filename, "files.tar.gz")
if err != nil { if err != nil {
return err, nil return err
} }
defer tarballFile.file.Close() defer tarballFile.file.Close()
archive, err := gzip.NewReader(tarballFile.tarReader) archive, err := gzip.NewReader(tarballFile.tarReader)
if err != nil { if err != nil {
return err, nil return err
} }
packageFilesReader := tar.NewReader(archive) packageFilesReader := tar.NewReader(archive)
for { for {
@ -504,15 +505,14 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
break break
} }
if err != nil { if err != nil {
return err, nil return err
} }
extractFilename := path.Join(rootDir, header.Name) extractFilename := path.Join(rootDir, header.Name)
switch header.Typeflag { switch header.Typeflag {
case tar.TypeDir: case tar.TypeDir:
files = append(files, strings.TrimPrefix(header.Name, "./"))
if err := os.Mkdir(extractFilename, 0755); err != nil { if err := os.Mkdir(extractFilename, 0755); err != nil {
if !os.IsExist(err) { if !os.IsExist(err) {
return err, nil return err
} }
} else { } else {
if verbose { if verbose {
@ -526,18 +526,16 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
if strings.HasSuffix(k, "/") { if strings.HasSuffix(k, "/") {
if strings.HasPrefix(header.Name, k) { if strings.HasPrefix(header.Name, k) {
if verbose { if verbose {
fmt.Println("Skipping File: " + extractFilename + " (Containing directory is set to be kept during installs/updates)") fmt.Println("Skipping File: " + extractFilename + " (Containing directory is set to be kept during reinstalls/updates)")
} }
files = append(files, strings.TrimPrefix(header.Name, "./"))
skip = true skip = true
continue continue
} }
} else { } else {
if header.Name == k { if header.Name == k {
if verbose { if verbose {
fmt.Println("Skipping File: " + extractFilename + " (File is configured to be kept during installs/updates)") fmt.Println("Skipping File: " + extractFilename + " (File is configured to be kept during reinstalls/updates)")
} }
files = append(files, strings.TrimPrefix(header.Name, "./"))
skip = true skip = true
continue continue
} }
@ -549,51 +547,48 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
} }
err := os.Remove(extractFilename) err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err, nil return err
} }
outFile, err := os.Create(extractFilename) outFile, err := os.Create(extractFilename)
if verbose { if verbose {
fmt.Println("Creating File: " + extractFilename) fmt.Println("Creating File: " + extractFilename)
} }
files = append(files, strings.TrimPrefix(header.Name, "./"))
if err != nil { if err != nil {
return err, nil return err
} }
if _, err := io.Copy(outFile, packageFilesReader); err != nil { if _, err := io.Copy(outFile, packageFilesReader); err != nil {
return err, nil return err
} }
if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil { if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil {
return err, nil return err
} }
err = outFile.Close() err = outFile.Close()
if err != nil { if err != nil {
return err, nil return err
} }
case tar.TypeSymlink: case tar.TypeSymlink:
if verbose { if verbose {
fmt.Println("Creating Symlink: " + extractFilename + " -> " + header.Linkname) fmt.Println("Creating Symlink: " + extractFilename + " -> " + header.Linkname)
} }
files = append(files, strings.TrimPrefix(header.Name, "./"))
err := os.Remove(extractFilename) err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err, nil return err
} }
err = os.Symlink(header.Linkname, extractFilename) err = os.Symlink(header.Linkname, extractFilename)
if err != nil { if err != nil {
return err, nil return err
} }
case tar.TypeLink: case tar.TypeLink:
if verbose { if verbose {
fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(rootDir, strings.TrimPrefix(header.Linkname, "files/"))) fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(rootDir, strings.TrimPrefix(header.Linkname, "files/")))
} }
files = append(files, strings.TrimPrefix(header.Name, "./"))
seenHardlinks[extractFilename] = path.Join(strings.TrimPrefix(header.Linkname, "files/")) seenHardlinks[extractFilename] = path.Join(strings.TrimPrefix(header.Linkname, "files/"))
err := os.Remove(extractFilename) err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err, nil return err
} }
default: default:
return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename), nil return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename)
} }
} }
for extractFilename, destination := range seenHardlinks { for extractFilename, destination := range seenHardlinks {
@ -602,12 +597,12 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
} }
err := os.Link(path.Join(rootDir, destination), extractFilename) err := os.Link(path.Join(rootDir, destination), extractFilename)
if err != nil { if err != nil {
return err, nil return err
} }
} }
defer archive.Close() defer archive.Close()
defer file.Close() defer file.Close()
return nil, files return nil
} }
func isSplitPackage(filename string) bool { func isSplitPackage(filename string) bool {
@ -1054,17 +1049,103 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc,
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
return err return err
} }
var oldFiles []string
var files []string
bpmpkg, err := ReadPackage(filename) bpmpkg, err := ReadPackage(filename)
if err != nil { if err != nil {
return err return err
} }
packageInstalled := IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) packageInstalled := IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir)
// Check if package is installed and remove current files
if packageInstalled { if packageInstalled {
// Fetching and reversing package file entry list
fileEntries := GetPackageFiles(bpmpkg.PkgInfo.Name, rootDir) fileEntries := GetPackageFiles(bpmpkg.PkgInfo.Name, rootDir)
sort.Slice(fileEntries, func(i, j int) bool {
return fileEntries[i].Path < fileEntries[j].Path
})
slices.Reverse(fileEntries)
files, err := GetAllPackageFiles(rootDir, bpmpkg.PkgInfo.Name)
if err != nil {
return err
}
// Removing old package files
if verbose {
fmt.Printf("Removing old files for package (%s)...\n", bpmpkg.PkgInfo.Name)
}
for _, entry := range fileEntries { for _, entry := range fileEntries {
files = append(files, entry.Path) file := path.Join(rootDir, entry.Path)
stat, err := os.Lstat(file)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
if len(files[entry.Path]) != 0 {
if verbose {
fmt.Println("Skipping path: " + file + " (Path is managed by multiple packages)")
}
continue
}
shouldContinue := false
for _, value := range bpmpkg.PkgInfo.Keep {
if strings.HasSuffix(value, "/") {
if strings.HasPrefix(entry.Path, value) || entry.Path == strings.TrimSuffix(value, "/") {
if verbose {
fmt.Println("Skipping path: " + file + " (Path is set to be kept during reinstalls/updates)")
}
shouldContinue = true
continue
}
} else {
if entry.Path == value {
if verbose {
fmt.Println("Skipping path: " + file + " (Path is set to be kept during reinstalls/updates)")
}
shouldContinue = true
continue
}
}
}
if shouldContinue {
continue
}
if stat.Mode()&os.ModeSymlink != 0 {
if verbose {
fmt.Println("Removing: " + file)
}
err := os.Remove(file)
if err != nil {
return err
}
continue
}
if stat.IsDir() {
dir, err := os.ReadDir(file)
if err != nil {
return err
}
if len(dir) != 0 {
if verbose {
fmt.Println("Skipping non-empty directory: " + file)
}
continue
}
if verbose {
fmt.Println("Removing: " + file)
}
err = os.Remove(file)
if err != nil {
return err
}
} else {
if verbose {
fmt.Println("Removing: " + file)
}
err := os.Remove(file)
if err != nil {
return err
}
}
} }
} }
if !force { if !force {
@ -1072,30 +1153,27 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc,
return errors.New("cannot install a package with a different architecture") return errors.New("cannot install a package with a different architecture")
} }
} }
if verbose {
fmt.Printf("Extracting files for package (%s)...\n", bpmpkg.PkgInfo.Name)
}
if bpmpkg.PkgInfo.Type == "binary" { if bpmpkg.PkgInfo.Type == "binary" {
err, i := extractPackage(bpmpkg, verbose, filename, rootDir) err := extractPackage(bpmpkg, verbose, filename, rootDir)
if err != nil { if err != nil {
return err return err
} }
files = i
} else if bpmpkg.PkgInfo.Type == "source" { } else if bpmpkg.PkgInfo.Type == "source" {
if isSplitPackage(filename) { if isSplitPackage(filename) {
return errors.New("BPM is unable to install split source packages") return errors.New("BPM is unable to install split source packages")
} }
err, i := compilePackage(bpmpkg, filename, rootDir, verbose, binaryPkgFromSrc, skipCheck, keepTempDir) err, _ := compilePackage(bpmpkg, filename, rootDir, verbose, binaryPkgFromSrc, skipCheck, keepTempDir)
if err != nil { if err != nil {
return err return err
} }
files = i
} else { } else {
return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type) return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type)
} }
slices.Sort(files)
slices.Reverse(files)
filesDiff := slices.DeleteFunc(oldFiles, func(f string) bool {
return slices.Contains(files, f)
})
installedDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
err = os.MkdirAll(installedDir, 0755) err = os.MkdirAll(installedDir, 0755)
@ -1164,79 +1242,6 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc,
} }
} }
if len(filesDiff) != 0 {
fmt.Println("Removing obsolete files...")
var symlinks []string
for _, f := range filesDiff {
f = path.Join(rootDir, f)
lstat, err := os.Lstat(f)
if os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
if lstat.Mode()&os.ModeSymlink != 0 {
symlinks = append(symlinks, f)
continue
}
stat, err := os.Stat(f)
if os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
if stat.IsDir() {
dir, err := os.ReadDir(f)
if err != nil {
return err
}
if len(dir) == 0 {
if verbose {
fmt.Println("Removing: " + f)
}
err := os.Remove(f)
if err != nil {
return err
}
}
} else {
if verbose {
fmt.Println("Removing: " + f)
}
err := os.Remove(f)
if err != nil {
return err
}
}
}
removals := -1
for len(symlinks) > 0 && removals != 0 {
removals = 0
for i := len(symlinks) - 1; i >= 0; i-- {
f := symlinks[i]
f = path.Join(rootDir, f)
_, err := os.Lstat(f)
if os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
_, err = filepath.EvalSymlinks(f)
if os.IsNotExist(err) {
err := os.Remove(f)
if err != nil {
return err
}
removals++
if verbose {
fmt.Println("Removing: " + f)
}
} else if err != nil {
return err
}
}
}
}
if !packageInstalled { if !packageInstalled {
err = ExecutePackageScripts(filename, rootDir, Install, true) err = ExecutePackageScripts(filename, rootDir, Install, true)
if err != nil { if err != nil {
@ -1400,7 +1405,7 @@ func GetPackageFiles(pkg, rootDir string) []*PackageFileEntry {
stringEntry := strings.Split(strings.TrimSpace(line), " ") stringEntry := strings.Split(strings.TrimSpace(line), " ")
if len(stringEntry) < 5 { if len(stringEntry) < 5 {
pkgFiles = append(pkgFiles, &PackageFileEntry{ pkgFiles = append(pkgFiles, &PackageFileEntry{
Path: line, Path: strings.TrimSuffix(line, "/"),
OctalPerms: 0, OctalPerms: 0,
UserID: 0, UserID: 0,
GroupID: 0, GroupID: 0,
@ -1425,7 +1430,7 @@ func GetPackageFiles(pkg, rootDir string) []*PackageFileEntry {
return nil return nil
} }
pkgFiles = append(pkgFiles, &PackageFileEntry{ pkgFiles = append(pkgFiles, &PackageFileEntry{
Path: strings.Join(stringEntry[:len(stringEntry)-4], " "), Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"),
OctalPerms: uint32(octalPerms), OctalPerms: uint32(octalPerms),
UserID: int(uid), UserID: int(uid),
GroupID: int(gid), GroupID: int(gid),
@ -1472,6 +1477,34 @@ func GetPackage(pkg, rootDir string) *BPMPackage {
} }
} }
func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) {
ret := make(map[string][]*BPMPackage)
pkgNames, err := GetInstalledPackages(rootDir)
if err != nil {
return nil, err
}
for _, pkgName := range pkgNames {
if slices.Contains(excludePackages, pkgName) {
continue
}
bpmpkg := GetPackage(pkgName, rootDir)
if bpmpkg == nil {
return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName))
}
for _, entry := range bpmpkg.PkgFiles {
if _, ok := ret[entry.Path]; ok {
ret[entry.Path] = append(ret[entry.Path], bpmpkg)
} else {
ret[entry.Path] = []*BPMPackage{bpmpkg}
}
}
}
return ret, nil
}
func RemovePackage(pkg string, verbose bool, rootDir string) error { func RemovePackage(pkg string, verbose bool, rootDir string) error {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg) pkgDir := path.Join(installedDir, pkg)
@ -1479,8 +1512,19 @@ func RemovePackage(pkg string, verbose bool, rootDir string) error {
if pkgInfo == nil { if pkgInfo == nil {
return errors.New("could not get package info") return errors.New("could not get package info")
} }
// Fetching and reversing package file entry list
fileEntries := GetPackageFiles(pkg, rootDir) fileEntries := GetPackageFiles(pkg, rootDir)
var symlinks []string sort.Slice(fileEntries, func(i, j int) bool {
return fileEntries[i].Path < fileEntries[j].Path
})
slices.Reverse(fileEntries)
files, err := GetAllPackageFiles(rootDir, pkg)
if err != nil {
return err
}
// Removing package files
for _, entry := range fileEntries { for _, entry := range fileEntries {
file := path.Join(rootDir, entry.Path) file := path.Join(rootDir, entry.Path)
lstat, err := os.Lstat(file) lstat, err := os.Lstat(file)
@ -1490,8 +1534,20 @@ func RemovePackage(pkg string, verbose bool, rootDir string) error {
if err != nil { if err != nil {
return err return err
} }
if len(files[entry.Path]) != 0 {
if verbose {
fmt.Println("Skipping path: " + file + "(Path is managed by multiple packages)")
}
continue
}
if lstat.Mode()&os.ModeSymlink != 0 { if lstat.Mode()&os.ModeSymlink != 0 {
symlinks = append(symlinks, file) if verbose {
fmt.Println("Removing: " + file)
}
err := os.Remove(file)
if err != nil {
return err
}
continue continue
} }
stat, err := os.Stat(file) stat, err := os.Stat(file)
@ -1506,15 +1562,19 @@ func RemovePackage(pkg string, verbose bool, rootDir string) error {
if err != nil { if err != nil {
return err return err
} }
if len(dir) == 0 { if len(dir) != 0 {
if verbose {
fmt.Println("Skipping non-empty directory: " + file)
}
continue
}
if verbose { if verbose {
fmt.Println("Removing: " + file) fmt.Println("Removing: " + file)
} }
err := os.Remove(file) err = os.Remove(file)
if err != nil { if err != nil {
return err return err
} }
}
} else { } else {
if verbose { if verbose {
fmt.Println("Removing: " + file) fmt.Println("Removing: " + file)
@ -1525,33 +1585,8 @@ func RemovePackage(pkg string, verbose bool, rootDir string) error {
} }
} }
} }
removals := -1
for len(symlinks) > 0 && removals != 0 { // Executing post_remove script
removals = 0
for i := len(symlinks) - 1; i >= 0; i-- {
file := symlinks[i]
file = path.Join(rootDir, file)
_, err := os.Lstat(file)
if os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
_, err = filepath.EvalSymlinks(file)
if os.IsNotExist(err) {
err := os.Remove(file)
if err != nil {
return err
}
removals++
if verbose {
fmt.Println("Removing: " + file)
}
} else if err != nil {
return err
}
}
}
if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil { if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil {
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh")) cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh"))
if !BPMConfig.SilentCompilation { if !BPMConfig.SilentCompilation {
@ -1586,12 +1621,15 @@ func RemovePackage(pkg string, verbose bool, rootDir string) error {
return err return err
} }
} }
err := os.RemoveAll(pkgDir)
if err != nil { // Removing package directory
return err
}
if verbose { if verbose {
fmt.Println("Removing: " + pkgDir) fmt.Println("Removing: " + pkgDir)
} }
err = os.RemoveAll(pkgDir)
if err != nil {
return err
}
return nil return nil
} }