Merge pull request #50404 from thaJeztah/saner_copy_defaults

daemon: ContainerExtractToDir: make AllowOverwriteDirWithFile opt-in
This commit is contained in:
Paweł Gronowski
2025-07-24 12:35:25 +02:00
committed by GitHub
7 changed files with 20 additions and 18 deletions

View File

@@ -57,16 +57,16 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io
// ContainerExtractToDir extracts the given archive to the specified location
// in the filesystem of the container identified by the given name. The given
// path must be of a directory in the container. If it is not, the error will
// be an errdefs.InvalidParameter. If noOverwriteDirNonDir is true then it will
// be an error if unpacking the given content would cause an existing directory
// to be replaced with a non-directory and vice versa.
func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error {
// be an errdefs.InvalidParameter. It returns an error if unpacking the given
// content would cause an existing directory to be replaced with a non-directory
// or vice versa, unless allowOverwriteDirWithFile is set to true.
func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, allowOverwriteDirWithFile bool, content io.Reader) error {
ctr, err := daemon.GetContainer(name)
if err != nil {
return err
}
err = daemon.containerExtractToDir(ctr, path, copyUIDGID, noOverwriteDirNonDir, content)
err = daemon.containerExtractToDir(ctr, path, copyUIDGID, allowOverwriteDirWithFile, content)
if err != nil {
if os.IsNotExist(err) {
return containerFileNotFound{path, name}

View File

@@ -6,9 +6,9 @@ import (
// defaultTarCopyOptions is the setting that is used when unpacking an archive
// for a copy API event.
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
func (daemon *Daemon) defaultTarCopyOptions(allowOverwriteDirWithFile bool) *archive.TarOptions {
return &archive.TarOptions{
NoOverwriteDirNonDir: noOverwriteDirNonDir,
NoOverwriteDirNonDir: !allowOverwriteDirWithFile,
IDMap: daemon.idMapping,
}
}

View File

@@ -14,9 +14,9 @@ import (
"github.com/moby/sys/user"
)
func (daemon *Daemon) tarCopyOptions(ctr *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
func (daemon *Daemon) tarCopyOptions(ctr *container.Container, allowOverwriteDirWithFile bool) (*archive.TarOptions, error) {
if ctr.Config.User == "" {
return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
return daemon.defaultTarCopyOptions(allowOverwriteDirWithFile), nil
}
uid, gid, err := getUIDGID(ctr.Config.User)
@@ -25,7 +25,7 @@ func (daemon *Daemon) tarCopyOptions(ctr *container.Container, noOverwriteDirNon
}
return &archive.TarOptions{
NoOverwriteDirNonDir: noOverwriteDirNonDir,
NoOverwriteDirNonDir: !allowOverwriteDirWithFile,
ChownOpts: &archive.ChownOpts{UID: uid, GID: gid},
}, nil
}

View File

@@ -97,7 +97,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
// noOverwriteDirNonDir is true then it will be an error if unpacking the
// given content would cause an existing directory to be replaced with a non-
// directory and vice versa.
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error {
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, allowOverwriteDirWithFile bool, content io.Reader) error {
container.Lock()
defer container.Unlock()
@@ -131,13 +131,13 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
return err
}
options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
options := daemon.defaultTarCopyOptions(allowOverwriteDirWithFile)
if copyUIDGID {
var err error
// tarCopyOptions will appropriately pull in the right uid/gid for the
// user/group and will set the options.
options, err = daemon.tarCopyOptions(container, noOverwriteDirNonDir)
options, err = daemon.tarCopyOptions(container, allowOverwriteDirWithFile)
if err != nil {
return err
}

View File

@@ -148,7 +148,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
// directory and vice versa.
//
// FIXME(thaJeztah): copyUIDGID is not supported on Windows, but currently ignored silently
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error {
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, allowOverwriteDirWithFile bool, content io.Reader) error {
container.Lock()
defer container.Unlock()
@@ -213,7 +213,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
// return err
// }
options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
options := daemon.defaultTarCopyOptions(allowOverwriteDirWithFile)
if err := chrootarchive.UntarWithRoot(content, resolvedPath, options, container.BaseFS); err != nil {
return err
}

View File

@@ -23,7 +23,7 @@ type execBackend interface {
type copyBackend interface {
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *container.PathStat, err error)
ContainerExport(ctx context.Context, name string, out io.Writer) error
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
ContainerExtractToDir(name, path string, copyUIDGID, allowOverwriteDirWithFile bool, content io.Reader) error
ContainerStatPath(name string, path string) (stat *container.PathStat, err error)
}

View File

@@ -92,8 +92,10 @@ func (c *containerRouter) putContainersArchive(ctx context.Context, w http.Respo
return err
}
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
// TODO(thaJeztah): reverse the default: deprecate noOverwriteDirNonDir and add "allowOverwriteDirWithFile" (or similar) argument to opt-in.
allowOverwriteDirWithFile := !r.Form.Has("noOverwriteDirNonDir") || !httputils.BoolValue(r, "noOverwriteDirNonDir")
copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
return c.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
return c.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, allowOverwriteDirWithFile, r.Body)
}