package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( "path/filepath" "strconv" "strings" "github.com/docker/docker/pkg/idtools" "github.com/moby/sys/symlink" lcUser "github.com/opencontainers/runc/libcontainer/user" "github.com/pkg/errors" ) func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) { var userStr, grpStr string parts := strings.Split(chown, ":") if len(parts) > 2 { return idtools.Identity{}, errors.New("invalid chown string format: " + chown) } if len(parts) == 1 { // if no group specified, use the user spec as group as well userStr, grpStr = parts[0], parts[0] } else { userStr, grpStr = parts[0], parts[1] } passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath) if err != nil { return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs") } groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath) if err != nil { return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs") } uid, err := lookupUser(userStr, passwdPath) if err != nil { return idtools.Identity{}, errors.Wrapf(err, "can't find uid for user "+userStr) } gid, err := lookupGroup(grpStr, groupPath) if err != nil { return idtools.Identity{}, errors.Wrapf(err, "can't find gid for group "+grpStr) } // convert as necessary because of user namespaces chownPair, err := identityMapping.ToHost(idtools.Identity{UID: uid, GID: gid}) if err != nil { return idtools.Identity{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping") } return chownPair, nil } func lookupUser(userStr, filepath string) (int, error) { // if the string is actually a uid integer, parse to int and return // as we don't need to translate with the help of files uid, err := strconv.Atoi(userStr) if err == nil { return uid, nil } users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool { return u.Name == userStr }) if err != nil { return 0, err } if len(users) == 0 { return 0, errors.New("no such user: " + userStr) } return users[0].Uid, nil } func lookupGroup(groupStr, filepath string) (int, error) { // if the string is actually a gid integer, parse to int and return // as we don't need to translate with the help of files gid, err := strconv.Atoi(groupStr) if err == nil { return gid, nil } groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool { return g.Name == groupStr }) if err != nil { return 0, err } if len(groups) == 0 { return 0, errors.New("no such group: " + groupStr) } return groups[0].Gid, nil }