diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 388a5d54d6..d28fa69ad6 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -13,6 +13,7 @@ import ( "github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/config/types" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) const ( @@ -177,7 +178,7 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error { } // Save encodes and writes out all the authorization information -func (configFile *ConfigFile) Save() error { +func (configFile *ConfigFile) Save() (retErr error) { if configFile.Filename == "" { return errors.Errorf("Can't save config with empty filename") } @@ -190,12 +191,26 @@ func (configFile *ConfigFile) Save() error { if err != nil { return err } + defer func() { + temp.Close() + if retErr != nil { + if err := os.Remove(temp.Name()); err != nil { + logrus.WithError(err).WithField("file", temp.Name()).Debug("Error cleaning up temp file") + } + } + }() + err = configFile.SaveToWriter(temp) - temp.Close() if err != nil { - os.Remove(temp.Name()) return err } + + if err := temp.Close(); err != nil { + return errors.Wrap(err, "error closing temp file") + } + + // Try copying the current config file (if any) ownership and permissions + copyFilePermissions(configFile.Filename, temp.Name()) return os.Rename(temp.Name(), configFile.Filename) } diff --git a/cli/config/configfile/file_unix.go b/cli/config/configfile/file_unix.go new file mode 100644 index 0000000000..3ca65c6140 --- /dev/null +++ b/cli/config/configfile/file_unix.go @@ -0,0 +1,35 @@ +// +build !windows + +package configfile + +import ( + "os" + "syscall" +) + +// copyFilePermissions copies file ownership and permissions from "src" to "dst", +// ignoring any error during the process. +func copyFilePermissions(src, dst string) { + var ( + mode os.FileMode = 0600 + uid, gid int + ) + + fi, err := os.Stat(src) + if err != nil { + return + } + if fi.Mode().IsRegular() { + mode = fi.Mode() + } + if err := os.Chmod(dst, mode); err != nil { + return + } + + uid = int(fi.Sys().(*syscall.Stat_t).Uid) + gid = int(fi.Sys().(*syscall.Stat_t).Gid) + + if uid > 0 && gid > 0 { + _ = os.Chown(dst, uid, gid) + } +} diff --git a/cli/config/configfile/file_windows.go b/cli/config/configfile/file_windows.go new file mode 100644 index 0000000000..42fffc39ad --- /dev/null +++ b/cli/config/configfile/file_windows.go @@ -0,0 +1,5 @@ +package configfile + +func copyFilePermissions(src, dst string) { + // TODO implement for Windows +}