From 524bbf990efbbdfb68c98f8d53730e1680d5ee11 Mon Sep 17 00:00:00 2001 From: Daniele Rondina Date: Sat, 1 Feb 2020 17:58:23 +0100 Subject: [PATCH] Add support for same-owner config option --- cmd/root.go | 12 +++++ contrib/config/luet.yaml | 3 ++ pkg/compiler/artifact.go | 6 ++- pkg/config/config.go | 14 +++++- pkg/helpers/archive.go | 96 ++++++++++++++++++++++++++++++--------- pkg/helpers/repository.go | 3 +- 6 files changed, 107 insertions(+), 27 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index c3ff7b2f..37c34181 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,6 +17,7 @@ package cmd import ( "os" + "os/user" "path/filepath" "runtime" "strings" @@ -110,8 +111,19 @@ func init() { pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.luet.yaml)") pflags.BoolVarP(&Verbose, "verbose", "v", false, "verbose output") pflags.Bool("fatal", false, "Enables Warnings to exit") + + u, err := user.Current() + if err != nil { + Fatal("failed to retrieve user identity:", err.Error()) + } + sameOwner := false + if u.Uid == "0" { + sameOwner = true + } + pflags.Bool("same-owner", sameOwner, "Maintain same owner on uncompress.") pflags.Int("concurrency", runtime.NumCPU(), "Concurrency") + config.LuetCfg.Viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner")) config.LuetCfg.Viper.BindPFlag("general.debug", pflags.Lookup("verbose")) config.LuetCfg.Viper.BindPFlag("general.concurrency", pflags.Lookup("concurrency")) config.LuetCfg.Viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal")) diff --git a/contrib/config/luet.yaml b/contrib/config/luet.yaml index 7d7e25cc..9699e55c 100644 --- a/contrib/config/luet.yaml +++ b/contrib/config/luet.yaml @@ -35,6 +35,9 @@ # Enable warnings to exit # fatal_warnings: false # +# Try extracting tree/packages with the same ownership as exists in the archive (default for superuser). +# same_owner: false +# # --------------------------------------------- # System configuration section: # --------------------------------------------- diff --git a/pkg/compiler/artifact.go b/pkg/compiler/artifact.go index 11bed5c8..c06e69ea 100644 --- a/pkg/compiler/artifact.go +++ b/pkg/compiler/artifact.go @@ -31,6 +31,7 @@ import ( "strings" "sync" + . "github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/helpers" . "github.com/mudler/luet/pkg/logger" "github.com/mudler/luet/pkg/solver" @@ -294,14 +295,15 @@ func (a *PackageArtifact) Unpack(dst string, keepPerms bool) error { return errors.Wrap(err, "Cannot copy to "+a.GetPath()+".uncompressed") } - err = helpers.Untar(a.GetPath()+".uncompressed", dst, keepPerms) + err = helpers.Untar(a.GetPath()+".uncompressed", dst, + LuetCfg.GetGeneral().SameOwner) if err != nil { return err } return nil // Defaults to tar only (covers when "none" is supplied) default: - return helpers.Untar(a.GetPath(), dst, keepPerms) + return helpers.Untar(a.GetPath(), dst, LuetCfg.GetGeneral().SameOwner) } return errors.New("Compression type must be supplied") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 858de3f4..47973c29 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,6 +19,7 @@ package config import ( "errors" "fmt" + "os/user" "runtime" "time" @@ -34,6 +35,7 @@ type LuetLoggingConfig struct { } type LuetGeneralConfig struct { + SameOwner bool `mapstructure:"same_owner"` Concurrency int `mapstructure:"concurrency"` Debug bool `mapstructure:"debug"` ShowBuildOutput bool `mapstructure:"show_build_output"` @@ -125,7 +127,6 @@ func NewLuetConfig(viper *v.Viper) *LuetConfig { } func GenDefault(viper *v.Viper) { - viper.SetDefault("logging.level", "info") viper.SetDefault("logging.path", "") viper.SetDefault("logging.json_format", false) @@ -137,6 +138,13 @@ func GenDefault(viper *v.Viper) { viper.SetDefault("general.spinner_charset", 22) viper.SetDefault("general.fatal_warnings", false) + u, _ := user.Current() + if u.Uid == "0" { + viper.SetDefault("general.same_owner", true) + } else { + viper.SetDefault("general.same_owner", false) + } + viper.SetDefault("system.database_engine", "boltdb") viper.SetDefault("system.database_path", "/var/cache/luet") viper.SetDefault("system.rootfs", "/") @@ -182,11 +190,13 @@ func (c *LuetGeneralConfig) String() string { ans := fmt.Sprintf(` general: concurrency: %d + same_owner: %t debug: %t fatal_warnings: %t show_build_output: %t spinner_ms: %d - spinner_charset: %d`, c.Concurrency, c.Debug, c.FatalWarns, c.ShowBuildOutput, + spinner_charset: %d`, c.Concurrency, c.SameOwner, c.Debug, + c.FatalWarns, c.ShowBuildOutput, c.SpinnerMs, c.SpinnerCharset) return ans diff --git a/pkg/helpers/archive.go b/pkg/helpers/archive.go index 9fff82b8..d898bf0e 100644 --- a/pkg/helpers/archive.go +++ b/pkg/helpers/archive.go @@ -16,13 +16,14 @@ package helpers import ( + "archive/tar" "io" "os" - //"os/user" - //"strconv" + "path/filepath" + + . "github.com/mudler/luet/pkg/config" "github.com/docker/docker/pkg/archive" - //"github.com/docker/docker/pkg/idtools" ) func Tar(src, dest string) error { @@ -52,31 +53,82 @@ func Tar(src, dest string) error { // Untar just a wrapper around the docker functions func Untar(src, dest string, sameOwner bool) error { + var ans error + in, err := os.Open(src) if err != nil { return err } defer in.Close() - opts := &archive.TarOptions{ - // NOTE: NoLchown boolean is used for chmod of the symlink - // Probably it's needed set this always to true. - NoLchown: true, - ExcludePatterns: []string{"dev/"}, // prevent 'operation not permitted' + if LuetCfg.GetGeneral().SameOwner { + // PRE: i have root privileged. + + opts := &archive.TarOptions{ + // NOTE: NoLchown boolean is used for chmod of the symlink + // Probably it's needed set this always to true. + NoLchown: true, + ExcludePatterns: []string{"dev/"}, // prevent 'operation not permitted' + } + + ans = archive.Untar(in, dest, opts) + } else { + + var fileReader io.ReadCloser = in + + tr := tar.NewReader(fileReader) + for { + header, err := tr.Next() + + switch { + case err == io.EOF: + goto tarEof + case err != nil: + return err + case header == nil: + continue + } + + // the target location where the dir/file should be created + target := filepath.Join(dest, header.Name) + + // Check the file type + switch header.Typeflag { + + // if its a dir and it doesn't exist create it + case tar.TypeDir: + if _, err := os.Stat(target); err != nil { + if err := os.MkdirAll(target, 0755); err != nil { + return err + } + } + + // handle creation of file + case tar.TypeReg: + f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + if err != nil { + return err + } + + // copy over contents + if _, err := io.Copy(f, tr); err != nil { + return err + } + + // manually close here after each file operation; defering would cause each + // file close to wait until all operations have completed. + f.Close() + + case tar.TypeSymlink: + source := header.Linkname + err := os.Symlink(source, target) + if err != nil { + return err + } + } + } + tarEof: } - /* - u, err := user.Current() - if err != nil { - return err - } - // TODO: This seems not sufficient for untar with normal user. - if u.Uid != "0" { - uid, _ := strconv.Atoi(u.Uid) - gid, _ := strconv.Atoi(u.Gid) - opts.ChownOpts = &idtools.Identity{UID: uid, GID: gid} - } - */ - - return archive.Untar(in, dest, opts) + return ans } diff --git a/pkg/helpers/repository.go b/pkg/helpers/repository.go index 29212ad7..8462f701 100644 --- a/pkg/helpers/repository.go +++ b/pkg/helpers/repository.go @@ -34,7 +34,8 @@ func GetRepoDatabaseDirPath(name string) string { } func GetSystemRepoDatabaseDirPath() string { - dbpath := filepath.Join(config.LuetCfg.GetSystem().Rootfs, config.LuetCfg.GetSystem().DatabasePath) + dbpath := filepath.Join(config.LuetCfg.GetSystem().Rootfs, + config.LuetCfg.GetSystem().DatabasePath) err := os.MkdirAll(dbpath, os.ModePerm) if err != nil { panic(err)