1package dockerfile // import "github.com/docker/docker/builder/dockerfile"
2
3import (
4	"path/filepath"
5	"strconv"
6	"strings"
7
8	"github.com/docker/docker/pkg/idtools"
9	"github.com/moby/sys/symlink"
10	lcUser "github.com/opencontainers/runc/libcontainer/user"
11	"github.com/pkg/errors"
12)
13
14func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) {
15	var userStr, grpStr string
16	parts := strings.Split(chown, ":")
17	if len(parts) > 2 {
18		return idtools.Identity{}, errors.New("invalid chown string format: " + chown)
19	}
20	if len(parts) == 1 {
21		// if no group specified, use the user spec as group as well
22		userStr, grpStr = parts[0], parts[0]
23	} else {
24		userStr, grpStr = parts[0], parts[1]
25	}
26
27	passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
28	if err != nil {
29		return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
30	}
31	groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
32	if err != nil {
33		return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
34	}
35	uid, err := lookupUser(userStr, passwdPath)
36	if err != nil {
37		return idtools.Identity{}, errors.Wrapf(err, "can't find uid for user "+userStr)
38	}
39	gid, err := lookupGroup(grpStr, groupPath)
40	if err != nil {
41		return idtools.Identity{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
42	}
43
44	// convert as necessary because of user namespaces
45	chownPair, err := identityMapping.ToHost(idtools.Identity{UID: uid, GID: gid})
46	if err != nil {
47		return idtools.Identity{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
48	}
49	return chownPair, nil
50}
51
52func lookupUser(userStr, filepath string) (int, error) {
53	// if the string is actually a uid integer, parse to int and return
54	// as we don't need to translate with the help of files
55	uid, err := strconv.Atoi(userStr)
56	if err == nil {
57		return uid, nil
58	}
59	users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool {
60		return u.Name == userStr
61	})
62	if err != nil {
63		return 0, err
64	}
65	if len(users) == 0 {
66		return 0, errors.New("no such user: " + userStr)
67	}
68	return users[0].Uid, nil
69}
70
71func lookupGroup(groupStr, filepath string) (int, error) {
72	// if the string is actually a gid integer, parse to int and return
73	// as we don't need to translate with the help of files
74	gid, err := strconv.Atoi(groupStr)
75	if err == nil {
76		return gid, nil
77	}
78	groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool {
79		return g.Name == groupStr
80	})
81	if err != nil {
82		return 0, err
83	}
84	if len(groups) == 0 {
85		return 0, errors.New("no such group: " + groupStr)
86	}
87	return groups[0].Gid, nil
88}
89