Repository: moby/moby
File: daemon/archive_tarcopyoptions_unix.go (Go)

Code before:
package daemon

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/docker/docker/daemon/container"
	"github.com/docker/docker/errdefs"
	"github.com/moby/go-archive"
	"github.com/moby/sys/user"
)

func (daemon *Daemon) tarCopyOptions(ctr *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
	if ctr.Config.User == "" {
		return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
	}

	uid, gid, err := getUIDGID(ctr.Config.User)
	if err != nil {
		return nil, errdefs.InvalidParameter(err)
	}

	return &archive.TarOptions{
		NoOverwriteDirNonDir: noOverwriteDirNonDir,
		ChownOpts:            &archive.ChownOpts{UID: uid, GID: gid},
	}, nil
}

// getUIDGID resolves the UID and GID of a given container's Config.User,
// which can contain a user name (or ID) and, optionally, group (or ID).
//
// usergrp is a username or uid, and optional group, in the format `user[:group]`.
// Both `user` and `group` can be provided as an `uid` / `gid`, so the following
// formats are supported:
//
// - username            - valid username from /etc/passwd
// - username:groupname  - valid username; valid groupname from /etc/passwd, /etc/group
// - uid                 - 32-bit unsigned int valid Linux UID value
// - uid:gid             - uid value; 32-bit unsigned int Linux GID value
// - username:gid        - valid username from getent(1), gid value; 32-bit unsigned int Linux GID value
// - uid:groupname       - 32-bit unsigned int valid Linux UID value, valid groupname from /etc/group
//
// If only a username (or uid) is provided, an attempt is made to look up the gid
// for that username using /etc/passwd
func getUIDGID(ctrUser string) (uid int, gid int, _ error) {
	userNameOrID, groupNameOrID, _ := strings.Cut(ctrUser, ":")

	// Align with behavior of docker run, which treats an empty username
	// or groupname as default (0 (root)).

Review comment (suggestion):
FWIW; considering we should make pkg/archive follow;

- Add a `AllowOverWriteDirWithFile` option
- Deprecate `NoOverwriteDirNonDir` and make it the default


I opened a quick PR where I removed the option, and nothing looks to be breaking, but maybe there's some specific use-case that requires it (unpacking an archive to a directory or something); maybe someone knows?

- https://github.com/moby/moby/pull/50401

Code after:
package daemon

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/docker/docker/daemon/container"
	"github.com/docker/docker/errdefs"
	"github.com/moby/go-archive"
	"github.com/moby/sys/user"
)

func (daemon *Daemon) tarCopyOptions(ctr *container.Container, allowOverwriteDirWithFile bool) (*archive.TarOptions, error) {
	if ctr.Config.User == "" {
		return daemon.defaultTarCopyOptions(allowOverwriteDirWithFile), nil
	}

	uid, gid, err := getUIDGID(ctr.Config.User)
	if err != nil {
		return nil, errdefs.InvalidParameter(err)
	}

	return &archive.TarOptions{
		NoOverwriteDirNonDir: !allowOverwriteDirWithFile,
		ChownOpts:            &archive.ChownOpts{UID: uid, GID: gid},
	}, nil
}

// getUIDGID resolves the UID and GID of a given container's Config.User,
// which can contain a user name (or ID) and, optionally, group (or ID).
//
// usergrp is a username or uid, and optional group, in the format `user[:group]`.
// Both `user` and `group` can be provided as an `uid` / `gid`, so the following
// formats are supported:
//
// - username            - valid username from /etc/passwd
// - username:groupname  - valid username; valid groupname from /etc/passwd, /etc/group
// - uid                 - 32-bit unsigned int valid Linux UID value
// - uid:gid             - uid value; 32-bit unsigned int Linux GID value
// - username:gid        - valid username from getent(1), gid value; 32-bit unsigned int Linux GID value
// - uid:groupname       - 32-bit unsigned int valid Linux UID value, valid groupname from /etc/group
//
// If only a username (or uid) is provided, an attempt is made to look up the gid
// for that username using /etc/passwd
func getUIDGID(ctrUser string) (uid int, gid int, _ error) {
	userNameOrID, groupNameOrID, _ := strings.Cut(ctrUser, ":")

	// Align with behavior of docker run, which treats an empty username
	// or groupname as default (0 (root)).