aboutsummaryrefslogtreecommitdiffsponsor
diff options
context:
space:
mode:
authorSlack Coder <slackcoder@server.ky>2022-04-23 09:46:41 -0500
committerSlack Coder <slackcoder@server.ky>2022-04-23 11:32:42 -0500
commit5d4066dd1e90fa704b67384327af72b3ec121102 (patch)
tree28d7c331840f87c6954c80e469477d679868ad72
parentf79877dd20bbf5fa51c8c0df50101c2547b9e91d (diff)
downloadpkgtools-go-5d4066dd1e90fa704b67384327af72b3ec121102.tar.xz
installpkg: Avoid using /install on target dir
Use a temporary randomly named directory named like 'installpkg-2052825695' for the package's metadata in its '/install' directory. It will help allow running multiple instances on the same target root directory by removing a point of conflict. Attempt to place this directory in the host's default temporary directory. If it does not exist, use the host's root path.
-rw-r--r--archive.go124
-rw-r--r--filesystem.go15
-rw-r--r--installpkg.go88
-rw-r--r--pkgtools.go2
4 files changed, 167 insertions, 62 deletions
diff --git a/archive.go b/archive.go
index fa1886f..6610936 100644
--- a/archive.go
+++ b/archive.go
@@ -12,7 +12,7 @@ import (
"golang.org/x/sys/unix"
)
-type ArchiveReader interface {
+type TarReader interface {
Next() (*tar.Header, error)
Read([]byte) (int, error)
}
@@ -107,15 +107,78 @@ func tarCreateSymlink(root string, header *tar.Header) error {
return err
}
-type InstallArchiveCfg struct {
+type TarFilter interface {
+ FilterTar(header *tar.Header, r io.Reader) error
+}
+
+type TarFilterFunc func(
+ header *tar.Header,
+ r io.Reader,
+) error
+
+func (f TarFilterFunc) FilterTar(h *tar.Header, r io.Reader) error {
+ return f(h, r)
+}
+
+type TarCfg struct {
Root string
Chown bool
Chmod bool
}
-func InstallArchive(
- r ArchiveReader,
- cfg *InstallArchiveCfg,
+type TarExtracter struct {
+ cfg *TarCfg
+}
+
+func NewTarExtractor(cfg *TarCfg) *TarExtracter {
+ return &TarExtracter{cfg}
+}
+
+func (s *TarExtracter) FilterTar(
+ header *tar.Header,
+ r io.Reader,
+) error {
+ target := path.Join(s.cfg.Root, header.Name)
+
+ var err error
+ switch header.Typeflag {
+ case tar.TypeBlock:
+ err = tarCreateBlockDev(target, header)
+ case tar.TypeChar:
+ err = tarCreateCharDev(target, header)
+ case tar.TypeFifo:
+ err = tarCreateFifo(target, header)
+ case tar.TypeDir:
+ err = tarCreateDir(target)
+ case tar.TypeReg:
+ err = tarCreateReg(target, r, header)
+ case tar.TypeLink:
+ err = tarCreateLink(s.cfg.Root, header)
+ case tar.TypeSymlink:
+ err = tarCreateSymlink(s.cfg.Root, header)
+ default:
+ err = errors.Errorf("unhandled file type '%c'", header.Typeflag)
+ }
+ if err != nil {
+ return errors.Wrap(err, "unpacking tar archive")
+ }
+
+ if s.cfg.Chown {
+ if err = os.Chown(target, header.Uid, header.Gid); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ }
+ if s.cfg.Chmod {
+ if err = os.Chmod(target, header.FileInfo().Mode()); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ }
+ return nil
+}
+
+func FilterTar(
+ r TarReader,
+ filters ...TarFilter,
) error {
for {
header, err := r.Next()
@@ -127,40 +190,29 @@ func InstallArchive(
if header == nil {
continue
}
- target := path.Join(cfg.Root, header.Name)
-
- switch header.Typeflag {
- case tar.TypeBlock:
- err = tarCreateBlockDev(target, header)
- case tar.TypeChar:
- err = tarCreateCharDev(target, header)
- case tar.TypeFifo:
- err = tarCreateFifo(target, header)
- case tar.TypeDir:
- err = tarCreateDir(target)
- case tar.TypeReg:
- err = tarCreateReg(target, r, header)
- case tar.TypeLink:
- err = tarCreateLink(cfg.Root, header)
- case tar.TypeSymlink:
- err = tarCreateSymlink(cfg.Root, header)
- default:
- err = errors.Errorf("unhandled file type '%c'", header.Typeflag)
- }
- if err != nil {
- return errors.Wrap(err, "unpacking tar archive")
- }
- if cfg.Chown {
- if err = os.Chown(target, header.Uid, header.Gid); err != nil {
- fmt.Fprintln(os.Stderr, err)
- }
- }
- if cfg.Chmod {
- if err = os.Chmod(target, header.FileInfo().Mode()); err != nil {
- fmt.Fprintln(os.Stderr, err)
+
+ for _, f := range filters {
+ // TODO: how do we dup r?
+ err := f.FilterTar(header, r)
+ if err != nil {
+ return err
}
}
}
return nil
}
+
+func ExtractTar(
+ cfg *TarCfg,
+ fp string,
+) error {
+ f, err := os.Open(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ r := tar.NewReader(f)
+ err = FilterTar(r, NewTarExtractor(cfg))
+ return errors.Wrap(err, "extracting tar file")
+}
diff --git a/filesystem.go b/filesystem.go
index c33df15..dec07f3 100644
--- a/filesystem.go
+++ b/filesystem.go
@@ -4,7 +4,9 @@ import (
"io/fs"
"math"
"os"
+ "path/filepath"
"strconv"
+ "strings"
"time"
)
@@ -46,6 +48,19 @@ func IsDir(path string) (bool, error) {
return info.IsDir(), nil
}
+func IsParentDir(parent, sub string) (bool, error) {
+ up := ".." + string(os.PathSeparator)
+
+ rel, err := filepath.Rel(parent, sub)
+ if err != nil {
+ return false, err
+ }
+ if !strings.HasPrefix(rel, up) && rel != ".." {
+ return true, nil
+ }
+ return false, nil
+}
+
func IsSymlink(path string) (bool, error) {
info, err := os.Stat(path)
if err != nil {
diff --git a/installpkg.go b/installpkg.go
index 7354ff0..7e10e61 100644
--- a/installpkg.go
+++ b/installpkg.go
@@ -1,12 +1,14 @@
package pkgtools
import (
+ "archive/tar"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
+ "strings"
"time"
"github.com/juju/fslock"
@@ -99,7 +101,12 @@ func (s *InstallPkgFlags) SetEnvValues() {
}
}
-func runInstallScript(flags *InstallPkgFlags) error {
+func runInstallScript(flags *InstallPkgFlags, installPath string) error {
+ installPath, err := filepath.Abs(installPath)
+ if err != nil {
+ return err
+ }
+
l := fslock.New(FileLockPath(flags.LockDir, filepath.Base(PackageInstallScript)))
if err := l.Lock(); err != nil {
return err
@@ -116,7 +123,7 @@ func runInstallScript(flags *InstallPkgFlags) error {
}
defer os.Chdir(cwd)
- installScript := path.Join(flags.Root, PackageInstallScript)
+ installScript := path.Join(installPath, filepath.Base(PackageInstallScript))
if _, err := os.Stat(installScript); !os.IsNotExist(err) {
cmd := exec.Command("/bin/bash", installScript)
cmd.Stdin = os.Stdout
@@ -287,19 +294,65 @@ func writeToDatabase(target string, pkg string) error {
return nil
}
+// ExtractSlackwarePkg unarchives the slackware package into the given
+// directory. The package metadata, under the archives '/install' path, is put
+// into a temporary directory and returned.
+func ExtractSlackwarePkg(flags *InstallPkgFlags, fp string) (string, error) {
+ slackPkg, err := OpenSlackwarePkg(fp)
+ if err != nil {
+ return "", errors.Wrap(err, "extracting package")
+ }
+ defer slackPkg.Close()
+
+ toRoot := NewTarExtractor(&TarCfg{
+ Root: flags.Root,
+ Chmod: flags.chmod,
+ Chown: flags.chown,
+ })
+
+ tmpDir := os.TempDir()
+ if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
+ tmpDir = "/"
+ } else if err != nil {
+ return "", errors.Wrap(err, "extracting package")
+ }
+ tempInstallDir, err := os.MkdirTemp(tmpDir, "installpkg-*")
+ if err != nil {
+ return "", errors.Wrap(err, "extracting package")
+ }
+ toTempInstallDir := NewTarExtractor(&TarCfg{
+ Root: tempInstallDir,
+ Chmod: flags.chmod,
+ Chown: flags.chown,
+ })
+
+ err = FilterTar(
+ slackPkg,
+ TarFilterFunc(func(h *tar.Header, r io.Reader) error {
+ if ok, err := IsParentDir(PackageInstallPath, h.Name); ok {
+ h.Name = strings.TrimPrefix(h.Name, PackageInstallPath+"/")
+ if h.Name == "" {
+ h.Name = "."
+ }
+ return toTempInstallDir.FilterTar(h, r)
+ } else if err != nil {
+ return errors.Wrap(err, "installing package")
+ }
+
+ return toRoot.FilterTar(h, r)
+ }),
+ )
+ return tempInstallDir, errors.Wrap(err, "installing package")
+}
+
func InstallPkg(flags *InstallPkgFlags, pkgs ...string) error {
- // Apply default flag values
+ // TODO: Apply default flag values
err := initializeDirectories(flags)
if err != nil {
return err
}
- installPath := path.Join(flags.Root, PackageInstallPath)
- if _, err := os.Stat(installPath); !errors.Is(err, os.ErrNotExist) {
- return errors.Errorf("The '%s' directory exists and would be used by installpkg and removed. Please consider renaming the directory or using Slackware's installpkg.", installPath)
- }
-
for _, pkg := range pkgs {
fmt.Printf("installing %s\n", PackageBase(pkg))
err = writeToDatabase(flags.Root, pkg)
@@ -307,22 +360,7 @@ func InstallPkg(flags *InstallPkgFlags, pkgs ...string) error {
return errors.Wrap(err, "writing package to database")
}
- err = func() error {
- slackPkg, err := OpenSlackwarePkg(pkg)
- if err != nil {
- return err
- }
- defer slackPkg.Close()
- err = InstallArchive(
- slackPkg,
- &InstallArchiveCfg{
- Root: flags.Root,
- Chmod: flags.chmod,
- Chown: flags.chown,
- },
- )
- return errors.Wrap(err, "installing package")
- }()
+ installPath, err := ExtractSlackwarePkg(flags, pkg)
if err != nil {
return errors.Wrap(err, "writing package to database")
}
@@ -336,7 +374,7 @@ func InstallPkg(flags *InstallPkgFlags, pkgs ...string) error {
_ = runLDConfig(flags.LockDir)
// TODO: command should still run after failed 'install script' execution
- err = runInstallScript(flags)
+ err = runInstallScript(flags, installPath)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "running install script"))
}
diff --git a/pkgtools.go b/pkgtools.go
index aaffb75..5c8e928 100644
--- a/pkgtools.go
+++ b/pkgtools.go
@@ -219,7 +219,7 @@ type SlackwarePkg struct {
pkgInfo PackageInfo
}
-var _ ArchiveReader = (*SlackwarePkg)(nil)
+var _ TarReader = (*SlackwarePkg)(nil)
func OpenSlackwarePkg(fp string) (*SlackwarePkg, error) {
var pkg SlackwarePkg