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

Code before:
	}

	usr, err := lookupUser(ctr, userNameOrID)
	if err != nil {
		return 0, 0, errors.Wrapf(err, "failed to look up user %q in container", userNameOrID)
	}
	if !hasGroup || groupNameOrID == "" {
		// Use user's primary group
		return usr.Uid, usr.Gid, nil
	}

	groupID, err = lookupGID(ctr, groupNameOrID)
	if err != nil {
		return 0, 0, errors.Wrapf(err, "failed to look up group %q in container", groupNameOrID)
	}
	return usr.Uid, groupID, nil
}

// getIDOrName checks whether nameOrID is a ID (integer) or a Name.
// It assumes nameOrID is a name when failing to parse as integer,
// in which case a non-empty name is returned.
func getIDOrName(nameOrID string) (id int, name string) {
	if uid, err := strconv.ParseUint(nameOrID, 10, 32); err == nil && uid <= math.MaxInt32 {
		// uid provided
		return int(uid), ""
	}
	// not am id, assume name
	return 0, nameOrID
}

func lookupUser(ctr *container.Container, nameOrID string) (user.User, error) {
	passwdPath, err := resourcePath(ctr, user.GetPasswdPath)
	if err != nil {
		return user.User{}, err
	}
	userID, userName := getIDOrName(nameOrID)
	users, err := user.ParsePasswdFileFilter(passwdPath, func(entry user.User) bool {
		if userName != "" {
			return entry.Name == userName
		}
		return entry.Uid == userID
	})
	if err != nil {
		return user.User{}, err
	}
	if len(users) == 0 {
		return user.User{}, errors.New("no group found")
	}
	return users[0], nil
}


Review comment (suggestion):
Wondering if this is too  strict,  and if we should just use something  like https://github.com/moby/moby/blob/079b6e618742a495aebe98b4be235884f3509e25/vendor/github.com/moby/sys/user/lookup_unix.go#L137-L139




Code after:
	//	uid=33 gid=0(root) groups=0(root)
	if userNameOrID != "" {
		var err error
		uid, gid, err = lookupUser(userNameOrID)
		if err != nil {
			return 0, 0, err
		}
	}
	if groupNameOrID != "" {
		var err error
		gid, err = lookupGID(groupNameOrID)
		if err != nil {
			return 0, 0, err
		}
	}
	return uid, gid, nil
}

// getIDOrName checks whether nameOrID is a ID (integer) or a Name.
// It assumes nameOrID is a name when failing to parse as integer,
// in which case a non-empty name is returned.
func getIDOrName(nameOrID string) (id int, name string) {
	if uid, err := strconv.ParseUint(nameOrID, 10, 32); err == nil && uid <= math.MaxInt32 {
		// uid provided
		return int(uid), ""
	}
	// not an id, assume name
	return 0, nameOrID
}

func lookupUser(nameOrID string) (uid, gid int, _ error) {
	userID, userName := getIDOrName(nameOrID)
	if userName != "" {
		u, err := user.LookupUser(userName)
		if err != nil {
			return 0, 0, fmt.Errorf("failed to look up user %q in container: %w", userName, err)
		}
		return u.Uid, u.Gid, nil
	}

	u, err := user.LookupUid(userID)
	if err != nil {
		// Match behavior of "docker run": when using a UID for the
		// user, resolving the user and its primary group is best-effort.
		// If a user with the given UID is found, we use its primary
		// group, otherwise use it as-is and use the default (0) as
		//  GID.
		//
		//	docker run --rm --user 12345 ubuntu id
		//	uid=12345 gid=0(root) groups=0(root)
		//