Explorar el Código

Partial refactor of UID/GID usage to use a unified struct.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin hace 8 años
padre
commit
09cd96c5ad

+ 2 - 2
container/container.go

@@ -216,7 +216,7 @@ func (container *Container) WriteHostConfig() error {
 }
 
 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
-func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error {
+func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error {
 	if container.Config.WorkingDir == "" {
 		return nil
 	}
@@ -228,7 +228,7 @@ func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error {
 		return err
 	}
 
-	if err := idtools.MkdirAllNewAs(pth, 0755, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIDs); err != nil {
 		pthInfo, err2 := os.Stat(pth)
 		if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
 			return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)

+ 6 - 7
daemon/archive.go

@@ -375,7 +375,7 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
 
 	destExists := true
 	destDir := false
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
+	rootIDs, _ := daemon.idMappings.RootPair()
 
 	// Work in daemon-local OS specific file paths
 	destPath = filepath.FromSlash(destPath)
@@ -413,11 +413,10 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
 		destExists = false
 	}
 
-	uidMaps, gidMaps := daemon.GetUIDGIDMaps()
 	archiver := &archive.Archiver{
 		Untar:   chrootarchive.Untar,
-		UIDMaps: uidMaps,
-		GIDMaps: gidMaps,
+		UIDMaps: daemon.idMappings.UIDs(),
+		GIDMaps: daemon.idMappings.GIDs(),
 	}
 
 	src, err := os.Stat(fullSrcPath)
@@ -430,7 +429,7 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
 		if err := archiver.CopyWithTar(fullSrcPath, destPath); err != nil {
 			return err
 		}
-		return fixPermissions(fullSrcPath, destPath, rootUID, rootGID, destExists)
+		return fixPermissions(fullSrcPath, destPath, rootIDs.UID, rootIDs.GID, destExists)
 	}
 	if decompress && archive.IsArchivePath(fullSrcPath) {
 		// Only try to untar if it is a file and that we've been told to decompress (when ADD-ing a remote file)
@@ -459,12 +458,12 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
 		destPath = filepath.Join(destPath, filepath.Base(srcPath))
 	}
 
-	if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAllAndChownNew(filepath.Dir(destPath), 0755, rootIDs); err != nil {
 		return err
 	}
 	if err := archiver.CopyFileWithTar(fullSrcPath, destPath); err != nil {
 		return err
 	}
 
-	return fixPermissions(fullSrcPath, destPath, rootUID, rootGID, destExists)
+	return fixPermissions(fullSrcPath, destPath, rootIDs.UID, rootIDs.GID, destExists)
 }

+ 2 - 3
daemon/archive_tarcopyoptions.go

@@ -7,10 +7,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 {
-	uidMaps, gidMaps := daemon.GetUIDGIDMaps()
 	return &archive.TarOptions{
 		NoOverwriteDirNonDir: noOverwriteDirNonDir,
-		UIDMaps:              uidMaps,
-		GIDMaps:              gidMaps,
+		UIDMaps:              daemon.idMappings.UIDs(),
+		GIDMaps:              daemon.idMappings.GIDs(),
 	}
 }

+ 12 - 12
daemon/container_operations_unix.go

@@ -109,14 +109,14 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
 		}
 		c.ShmPath = "/dev/shm"
 	} else {
-		rootUID, rootGID := daemon.GetRemappedUIDGID()
+		rootIDs, _ := daemon.idMappings.RootPair()
 		if !c.HasMountFor("/dev/shm") {
 			shmPath, err := c.ShmResourcePath()
 			if err != nil {
 				return err
 			}
 
-			if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil {
+			if err := idtools.MkdirAllAndChown(shmPath, 0700, rootIDs); err != nil {
 				return err
 			}
 
@@ -128,7 +128,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
 			if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
 				return fmt.Errorf("mounting shm tmpfs: %s", err)
 			}
-			if err := os.Chown(shmPath, rootUID, rootGID); err != nil {
+			if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil {
 				return err
 			}
 		}
@@ -147,9 +147,9 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 	logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
 
 	// retrieve possible remapped range start for root UID, GID
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
+	rootIDs, _ := daemon.idMappings.RootPair()
 	// create tmpfs
-	if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAllAndChown(localMountPath, 0700, rootIDs); err != nil {
 		return errors.Wrap(err, "error creating secret local mount path")
 	}
 
@@ -164,7 +164,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 		}
 	}()
 
-	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootUID, rootGID)
+	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
 	if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
 		return errors.Wrap(err, "unable to setup secret mount")
 	}
@@ -183,7 +183,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 		// secrets are created in the SecretMountPath on the host, at a
 		// single level
 		fPath := c.SecretFilePath(*s)
-		if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
+		if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
 			return errors.Wrap(err, "error creating secret mount path")
 		}
 
@@ -208,7 +208,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 			return err
 		}
 
-		if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
+		if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
 			return errors.Wrap(err, "error setting ownership for secret")
 		}
 	}
@@ -232,9 +232,9 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
 	logrus.Debugf("configs: setting up config dir: %s", localPath)
 
 	// retrieve possible remapped range start for root UID, GID
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
+	rootIDs, _ := daemon.idMappings.RootPair()
 	// create tmpfs
-	if err := idtools.MkdirAllAs(localPath, 0700, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAllAndChown(localPath, 0700, rootIDs); err != nil {
 		return errors.Wrap(err, "error creating config dir")
 	}
 
@@ -261,7 +261,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
 
 		log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
 
-		if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
+		if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
 			return errors.Wrap(err, "error creating config path")
 		}
 
@@ -283,7 +283,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
 			return err
 		}
 
-		if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
+		if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
 			return errors.Wrap(err, "error setting ownership for config")
 		}
 	}

+ 3 - 3
daemon/create.go

@@ -117,14 +117,14 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
 		return nil, err
 	}
 
-	rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
+	rootIDs, err := daemon.idMappings.RootPair()
 	if err != nil {
 		return nil, err
 	}
-	if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
 		return nil, err
 	}
-	if err := idtools.MkdirAs(container.CheckpointDir(), 0700, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil {
 		return nil, err
 	}
 

+ 2 - 2
daemon/create_unix.go

@@ -22,8 +22,8 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
 	}
 	defer daemon.Unmount(container)
 
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
-	if err := container.SetupWorkingDirectory(rootUID, rootGID); err != nil {
+	rootIDs, _ := daemon.idMappings.RootPair()
+	if err := container.SetupWorkingDirectory(rootIDs); err != nil {
 		return err
 	}
 

+ 17 - 35
daemon/daemon.go

@@ -94,8 +94,7 @@ type Daemon struct {
 	seccompEnabled            bool
 	apparmorEnabled           bool
 	shutdown                  bool
-	uidMaps                   []idtools.IDMap
-	gidMaps                   []idtools.IDMap
+	idMappings                *idtools.IDMappings
 	layerStore                layer.Store
 	imageStore                image.Store
 	PluginStore               *plugin.Store // todo: remove
@@ -524,11 +523,11 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 		return nil, err
 	}
 
-	uidMaps, gidMaps, err := setupRemappedRoot(config)
+	idMappings, err := setupRemappedRoot(config)
 	if err != nil {
 		return nil, err
 	}
-	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
+	rootIDs, err := idMappings.RootPair()
 	if err != nil {
 		return nil, err
 	}
@@ -538,7 +537,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	}
 
 	// set up the tmpDir to use a canonical path
-	tmp, err := prepareTempDir(config.Root, rootUID, rootGID)
+	tmp, err := prepareTempDir(config.Root, rootIDs)
 	if err != nil {
 		return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err)
 	}
@@ -587,7 +586,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	}
 
 	daemonRepo := filepath.Join(config.Root, "containers")
-	if err := idtools.MkdirAllAs(daemonRepo, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
+	if err := idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs); err != nil && !os.IsExist(err) {
 		return nil, err
 	}
 
@@ -632,8 +631,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 		MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
 		GraphDriver:               driverName,
 		GraphDriverOptions:        config.GraphOptions,
-		UIDMaps:                   uidMaps,
-		GIDMaps:                   gidMaps,
+		IDMappings:                idMappings,
 		PluginGetter:              d.PluginStore,
 		ExperimentalEnabled:       config.Experimental,
 	})
@@ -665,7 +663,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	}
 
 	// Configure the volumes driver
-	volStore, err := d.configureVolumes(rootUID, rootGID)
+	volStore, err := d.configureVolumes(rootIDs)
 	if err != nil {
 		return nil, err
 	}
@@ -728,8 +726,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
 	d.EventsService = eventsService
 	d.volumes = volStore
 	d.root = config.Root
-	d.uidMaps = uidMaps
-	d.gidMaps = gidMaps
+	d.idMappings = idMappings
 	d.seccompEnabled = sysInfo.Seccomp
 	d.apparmorEnabled = sysInfo.AppArmor
 
@@ -970,25 +967,10 @@ func (daemon *Daemon) GraphDriverName() string {
 	return daemon.layerStore.DriverName()
 }
 
-// GetUIDGIDMaps returns the current daemon's user namespace settings
-// for the full uid and gid maps which will be applied to containers
-// started in this instance.
-func (daemon *Daemon) GetUIDGIDMaps() ([]idtools.IDMap, []idtools.IDMap) {
-	return daemon.uidMaps, daemon.gidMaps
-}
-
-// GetRemappedUIDGID returns the current daemon's uid and gid values
-// if user namespaces are in use for this daemon instance.  If not
-// this function will return "real" root values of 0, 0.
-func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
-	uid, gid, _ := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
-	return uid, gid
-}
-
 // prepareTempDir prepares and returns the default directory to use
 // for temporary files.
 // If it doesn't exist, it is created. If it exists, its content is removed.
-func prepareTempDir(rootDir string, rootUID, rootGID int) (string, error) {
+func prepareTempDir(rootDir string, rootIDs idtools.IDPair) (string, error) {
 	var tmpDir string
 	if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
 		tmpDir = filepath.Join(rootDir, "tmp")
@@ -1008,12 +990,12 @@ func prepareTempDir(rootDir string, rootUID, rootGID int) (string, error) {
 	}
 	// We don't remove the content of tmpdir if it's not the default,
 	// it may hold things that do not belong to us.
-	return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID)
+	return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIDs)
 }
 
 func (daemon *Daemon) setupInitLayer(initPath string) error {
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
-	return initlayer.Setup(initPath, rootUID, rootGID)
+	rootIDs, _ := daemon.idMappings.RootPair()
+	return initlayer.Setup(initPath, rootIDs)
 }
 
 func setDefaultMtu(conf *config.Config) {
@@ -1024,8 +1006,8 @@ func setDefaultMtu(conf *config.Config) {
 	conf.Mtu = config.DefaultNetworkMtu
 }
 
-func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore, error) {
-	volumesDriver, err := local.New(daemon.configStore.Root, rootUID, rootGID)
+func (daemon *Daemon) configureVolumes(rootIDs idtools.IDPair) (*store.VolumeStore, error) {
+	volumesDriver, err := local.New(daemon.configStore.Root, rootIDs)
 	if err != nil {
 		return nil, err
 	}
@@ -1171,16 +1153,16 @@ func CreateDaemonRoot(config *config.Config) error {
 		}
 	}
 
-	uidMaps, gidMaps, err := setupRemappedRoot(config)
+	idMappings, err := setupRemappedRoot(config)
 	if err != nil {
 		return err
 	}
-	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
+	rootIDs, err := idMappings.RootPair()
 	if err != nil {
 		return err
 	}
 
-	if err := setupDaemonRoot(config, realRoot, rootUID, rootGID); err != nil {
+	if err := setupDaemonRoot(config, realRoot, rootIDs); err != nil {
 		return err
 	}
 

+ 2 - 1
daemon/daemon_test.go

@@ -11,6 +11,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	_ "github.com/docker/docker/pkg/discovery/memory"
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/registrar"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/volume"
@@ -127,7 +128,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
 		return nil, err
 	}
 
-	volumesDriver, err := local.New(tmp, 0, 0)
+	volumesDriver, err := local.New(tmp, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		return nil, err
 	}

+ 12 - 14
daemon/daemon_unix.go

@@ -1026,40 +1026,38 @@ func parseRemappedRoot(usergrp string) (string, string, error) {
 	return username, groupname, nil
 }
 
-func setupRemappedRoot(config *config.Config) ([]idtools.IDMap, []idtools.IDMap, error) {
+func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
 	if runtime.GOOS != "linux" && config.RemappedRoot != "" {
-		return nil, nil, fmt.Errorf("User namespaces are only supported on Linux")
+		return nil, fmt.Errorf("User namespaces are only supported on Linux")
 	}
 
 	// if the daemon was started with remapped root option, parse
 	// the config option to the int uid,gid values
-	var (
-		uidMaps, gidMaps []idtools.IDMap
-	)
 	if config.RemappedRoot != "" {
 		username, groupname, err := parseRemappedRoot(config.RemappedRoot)
 		if err != nil {
-			return nil, nil, err
+			return nil, err
 		}
 		if username == "root" {
 			// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
 			// effectively
 			logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
-			return uidMaps, gidMaps, nil
+			return &idtools.IDMappings{}, nil
 		}
 		logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
 		// update remapped root setting now that we have resolved them to actual names
 		config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
 
-		uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname)
+		mappings, err := idtools.NewIDMappings(username, groupname)
 		if err != nil {
-			return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err)
+			return nil, errors.Wrapf(err, "Can't create ID mappings: %v")
 		}
+		return mappings, nil
 	}
-	return uidMaps, gidMaps, nil
+	return &idtools.IDMappings{}, nil
 }
 
-func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int) error {
+func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
 	config.Root = rootDir
 	// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
 	// so that syscalls executing as non-root, operating on subdirectories of the graph root
@@ -1084,10 +1082,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int
 	// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
 	// `chdir()` to work for containers namespaced to that uid/gid)
 	if config.RemappedRoot != "" {
-		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID))
+		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIDs.UID, rootIDs.GID))
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
 		// Create the root directory if it doesn't exist
-		if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil {
+		if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIDs); err != nil {
 			return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
 		}
 		// we also need to verify that any pre-existing directories in the path to
@@ -1100,7 +1098,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int
 			if dirPath == "/" {
 				break
 			}
-			if !idtools.CanAccess(dirPath, rootUID, rootGID) {
+			if !idtools.CanAccess(dirPath, rootIDs) {
 				return fmt.Errorf("A subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories.", config.Root)
 			}
 		}

+ 2 - 1
daemon/daemon_unix_test.go

@@ -11,6 +11,7 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/config"
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume/drivers"
 	"github.com/docker/docker/volume/local"
@@ -277,7 +278,7 @@ func TestMigratePre17Volumes(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	drv, err := local.New(volumeRoot, 0, 0)
+	drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}

+ 3 - 3
daemon/daemon_windows.go

@@ -454,11 +454,11 @@ func (daemon *Daemon) cleanupMounts() error {
 	return nil
 }
 
-func setupRemappedRoot(config *config.Config) ([]idtools.IDMap, []idtools.IDMap, error) {
-	return nil, nil, nil
+func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
+	return &idtools.IDMappings{}, nil
 }
 
-func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int) error {
+func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
 	config.Root = rootDir
 	// Create the root directory if it doesn't exists
 	if err := system.MkdirAllWithACL(config.Root, 0); err != nil && !os.IsExist(err) {

+ 2 - 3
daemon/export.go

@@ -40,11 +40,10 @@ func (daemon *Daemon) containerExport(container *container.Container) (io.ReadCl
 		return nil, err
 	}
 
-	uidMaps, gidMaps := daemon.GetUIDGIDMaps()
 	archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{
 		Compression: archive.Uncompressed,
-		UIDMaps:     uidMaps,
-		GIDMaps:     gidMaps,
+		UIDMaps:     daemon.idMappings.UIDs(),
+		GIDMaps:     daemon.idMappings.GIDs(),
 	})
 	if err != nil {
 		daemon.Unmount(container)

+ 2 - 2
daemon/info.go

@@ -72,8 +72,8 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 	if selinuxEnabled() {
 		securityOptions = append(securityOptions, "name=selinux")
 	}
-	uid, gid := daemon.GetRemappedUIDGID()
-	if uid != 0 || gid != 0 {
+	rootIDs, _ := daemon.idMappings.RootPair()
+	if rootIDs.UID != 0 || rootIDs.GID != 0 {
 		securityOptions = append(securityOptions, "name=userns")
 	}
 

+ 4 - 4
daemon/initlayer/setup_unix.go

@@ -16,7 +16,7 @@ import (
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // the container from unwanted side-effects on the rw layer.
-func Setup(initLayer string, rootUID, rootGID int) error {
+func Setup(initLayer string, rootIDs idtools.IDPair) error {
 	for pth, typ := range map[string]string{
 		"/dev/pts":         "dir",
 		"/dev/shm":         "dir",
@@ -38,12 +38,12 @@ func Setup(initLayer string, rootUID, rootGID int) error {
 
 		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
 			if os.IsNotExist(err) {
-				if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootUID, rootGID); err != nil {
+				if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIDs); err != nil {
 					return err
 				}
 				switch typ {
 				case "dir":
-					if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, pth), 0755, rootUID, rootGID); err != nil {
+					if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIDs); err != nil {
 						return err
 					}
 				case "file":
@@ -51,7 +51,7 @@ func Setup(initLayer string, rootUID, rootGID int) error {
 					if err != nil {
 						return err
 					}
-					f.Chown(rootUID, rootGID)
+					f.Chown(rootIDs.UID, rootIDs.GID)
 					f.Close()
 				default:
 					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {

+ 5 - 1
daemon/initlayer/setup_windows.go

@@ -2,12 +2,16 @@
 
 package initlayer
 
+import (
+	"github.com/docker/docker/pkg/idtools"
+)
+
 // Setup populates a directory with mountpoints suitable
 // for bind-mounting dockerinit into the container. The mountpoint is simply an
 // empty file at /.dockerinit
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // the container from unwanted side-effects on the rw layer.
-func Setup(initLayer string, rootUID, rootGID int) error {
+func Setup(initLayer string, rootIDs idtools.IDPair) error {
 	return nil
 }

+ 5 - 5
daemon/oci_linux.go

@@ -271,13 +271,13 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
 	userNS := false
 	// user
 	if c.HostConfig.UsernsMode.IsPrivate() {
-		uidMap, gidMap := daemon.GetUIDGIDMaps()
+		uidMap := daemon.idMappings.UIDs()
 		if uidMap != nil {
 			userNS = true
 			ns := specs.LinuxNamespace{Type: "user"}
 			setNamespace(s, ns)
 			s.Linux.UIDMappings = specMapping(uidMap)
-			s.Linux.GIDMappings = specMapping(gidMap)
+			s.Linux.GIDMappings = specMapping(daemon.idMappings.GIDs())
 		}
 	}
 	// network
@@ -591,7 +591,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
 
 	// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
 	// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
-	if uidMap, _ := daemon.GetUIDGIDMaps(); uidMap != nil || c.HostConfig.Privileged {
+	if uidMap := daemon.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged {
 		for i, m := range s.Mounts {
 			if m.Type == "cgroup" {
 				clearReadOnly(&s.Mounts[i])
@@ -611,8 +611,8 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
 		Path:     c.BaseFS,
 		Readonly: c.HostConfig.ReadonlyRootfs,
 	}
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
-	if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
+	rootIDs, _ := daemon.idMappings.RootPair()
+	if err := c.SetupWorkingDirectory(rootIDs); err != nil {
 		return err
 	}
 	cwd := c.Config.WorkingDir

+ 2 - 2
daemon/oci_solaris.go

@@ -130,8 +130,8 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
 		Path:     filepath.Dir(c.BaseFS),
 		Readonly: c.HostConfig.ReadonlyRootfs,
 	}
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
-	if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
+	rootIDs, _ := daemon.idMappings.RootPair()
+	if err := c.SetupWorkingDirectory(rootIDs); err != nil {
 		return err
 	}
 	cwd := c.Config.WorkingDir

+ 4 - 4
daemon/volumes_unix.go

@@ -54,8 +54,8 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 			return nil
 		}
 
-		rootUID, rootGID := daemon.GetRemappedUIDGID()
-		path, err := m.Setup(c.MountLabel, rootUID, rootGID, checkfunc)
+		rootIDs, _ := daemon.idMappings.RootPair()
+		path, err := m.Setup(c.MountLabel, rootIDs, checkfunc)
 		if err != nil {
 			return nil, err
 		}
@@ -85,9 +85,9 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 	// if we are going to mount any of the network files from container
 	// metadata, the ownership must be set properly for potential container
 	// remapped root (user namespaces)
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
+	rootIDs, _ := daemon.idMappings.RootPair()
 	for _, mount := range netMounts {
-		if err := os.Chown(mount.Source, rootUID, rootGID); err != nil {
+		if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil {
 			return nil, err
 		}
 	}

+ 2 - 1
daemon/volumes_windows.go

@@ -6,6 +6,7 @@ import (
 	"sort"
 
 	"github.com/docker/docker/container"
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/volume"
 )
 
@@ -24,7 +25,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
 			return nil, err
 		}
-		s, err := mount.Setup(c.MountLabel, 0, 0, nil)
+		s, err := mount.Setup(c.MountLabel, idtools.IDPair{0, 0}, nil)
 		if err != nil {
 			return nil, err
 		}

+ 2 - 2
daemon/workdir.go

@@ -16,6 +16,6 @@ func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
 		return err
 	}
 	defer daemon.Unmount(container)
-	rootUID, rootGID := daemon.GetRemappedUIDGID()
-	return container.SetupWorkingDirectory(rootUID, rootGID)
+	rootIDs, _ := daemon.idMappings.RootPair()
+	return container.SetupWorkingDirectory(rootIDs)
 }

+ 3 - 4
layer/layer_store.go

@@ -44,8 +44,7 @@ type StoreOptions struct {
 	MetadataStorePathTemplate string
 	GraphDriver               string
 	GraphDriverOptions        []string
-	UIDMaps                   []idtools.IDMap
-	GIDMaps                   []idtools.IDMap
+	IDMappings                *idtools.IDMappings
 	PluginGetter              plugingetter.PluginGetter
 	ExperimentalEnabled       bool
 }
@@ -55,8 +54,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
 	driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
 		Root:                options.StorePath,
 		DriverOptions:       options.GraphDriverOptions,
-		UIDMaps:             options.UIDMaps,
-		GIDMaps:             options.GIDMaps,
+		UIDMaps:             options.IDMappings.UIDs(),
+		GIDMaps:             options.IDMappings.GIDs(),
 		ExperimentalEnabled: options.ExperimentalEnabled,
 	})
 	if err != nil {

+ 63 - 7
pkg/idtools/idtools.go

@@ -37,6 +37,7 @@ const (
 // MkdirAllAs creates a directory (include any along the path) and then modifies
 // ownership to the requested uid/gid.  If the directory already exists, this
 // function will still change ownership to the requested uid/gid pair.
+// Deprecated: Use MkdirAllAndChown
 func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
 	return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
 }
@@ -44,16 +45,38 @@ func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
 // MkdirAllNewAs creates a directory (include any along the path) and then modifies
 // ownership ONLY of newly created directories to the requested uid/gid. If the
 // directories along the path exist, no change of ownership will be performed
+// Deprecated: Use MkdirAllAndChownNew
 func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
 	return mkdirAs(path, mode, ownerUID, ownerGID, true, false)
 }
 
 // MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
 // If the directory already exists, this function still changes ownership
+// Deprecated: Use MkdirAndChown with a IDPair
 func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
 	return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
 }
 
+// MkdirAllAndChown creates a directory (include any along the path) and then modifies
+// ownership to the requested uid/gid.  If the directory already exists, this
+// function will still change ownership to the requested uid/gid pair.
+func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error {
+	return mkdirAs(path, mode, ids.UID, ids.GID, true, true)
+}
+
+// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
+// If the directory already exists, this function still changes ownership
+func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error {
+	return mkdirAs(path, mode, ids.UID, ids.GID, false, true)
+}
+
+// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
+// ownership ONLY of newly created directories to the requested uid/gid. If the
+// directories along the path exist, no change of ownership will be performed
+func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error {
+	return mkdirAs(path, mode, ids.UID, ids.GID, true, false)
+}
+
 // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
 // If the maps are empty, then the root uid/gid will default to "real" 0/0
 func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
@@ -108,26 +131,59 @@ func ToHost(contID int, idMap []IDMap) (int, error) {
 	return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
 }
 
-// CreateIDMappings takes a requested user and group name and
+// IDPair is a UID and GID pair
+type IDPair struct {
+	UID int
+	GID int
+}
+
+// IDMappings contains a mappings of UIDs and GIDs
+type IDMappings struct {
+	uids []IDMap
+	gids []IDMap
+}
+
+// NewIDMappings takes a requested user and group name and
 // using the data from /etc/sub{uid,gid} ranges, creates the
 // proper uid and gid remapping ranges for that user/group pair
-func CreateIDMappings(username, groupname string) ([]IDMap, []IDMap, error) {
+func NewIDMappings(username, groupname string) (*IDMappings, error) {
 	subuidRanges, err := parseSubuid(username)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	subgidRanges, err := parseSubgid(groupname)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	if len(subuidRanges) == 0 {
-		return nil, nil, fmt.Errorf("No subuid ranges found for user %q", username)
+		return nil, fmt.Errorf("No subuid ranges found for user %q", username)
 	}
 	if len(subgidRanges) == 0 {
-		return nil, nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
+		return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
 	}
 
-	return createIDMap(subuidRanges), createIDMap(subgidRanges), nil
+	return &IDMappings{
+		uids: createIDMap(subuidRanges),
+		gids: createIDMap(subgidRanges),
+	}, nil
+}
+
+// RootPair returns a uid and gid pair for the root user
+func (i *IDMappings) RootPair() (IDPair, error) {
+	uid, gid, err := GetRootUIDGID(i.uids, i.gids)
+	return IDPair{UID: uid, GID: gid}, err
+}
+
+// UIDs return the UID mapping
+// TODO: remove this once everything has been refactored to use pairs
+func (i *IDMappings) UIDs() []IDMap {
+	return i.uids
+}
+
+// GIDs return the UID mapping
+// TODO: remove this once everything has been refactored to use pairs
+func (i *IDMappings) GIDs() []IDMap {
+	return i.gids
 }
 
 func createIDMap(subidRanges ranges) []IDMap {

+ 3 - 3
pkg/idtools/idtools_unix.go

@@ -69,15 +69,15 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
 
 // CanAccess takes a valid (existing) directory and a uid, gid pair and determines
 // if that uid, gid pair has access (execute bit) to the directory
-func CanAccess(path string, uid, gid int) bool {
+func CanAccess(path string, pair IDPair) bool {
 	statInfo, err := system.Stat(path)
 	if err != nil {
 		return false
 	}
 	fileMode := os.FileMode(statInfo.Mode())
 	permBits := fileMode.Perm()
-	return accessible(statInfo.UID() == uint32(uid),
-		statInfo.GID() == uint32(gid), permBits)
+	return accessible(statInfo.UID() == uint32(pair.UID),
+		statInfo.GID() == uint32(pair.GID), permBits)
 }
 
 func accessible(isOwner, isGroup bool, perms os.FileMode) bool {

+ 1 - 1
pkg/idtools/idtools_windows.go

@@ -20,6 +20,6 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
 // CanAccess takes a valid (existing) directory and a uid, gid pair and determines
 // if that uid, gid pair has access (execute bit) to the directory
 // Windows does not require/support this function, so always return true
-func CanAccess(path string, uid, gid int) bool {
+func CanAccess(path string, pair IDPair) bool {
 	return true
 }

+ 2 - 1
plugin/manager_linux.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/daemon/initlayer"
 	"github.com/docker/docker/libcontainerd"
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/stringid"
@@ -58,7 +59,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
 		}
 	}
 
-	if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), 0, 0); err != nil {
+	if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), idtools.IDPair{0, 0}); err != nil {
 		return errors.WithStack(err)
 	}
 

+ 5 - 7
volume/local/local.go

@@ -55,10 +55,10 @@ type activeMount struct {
 // New instantiates a new Root instance with the provided scope. Scope
 // is the base path that the Root instance uses to store its
 // volumes. The base path is created here if it does not exist.
-func New(scope string, rootUID, rootGID int) (*Root, error) {
+func New(scope string, rootIDs idtools.IDPair) (*Root, error) {
 	rootDirectory := filepath.Join(scope, volumesPathName)
 
-	if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
+	if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIDs); err != nil {
 		return nil, err
 	}
 
@@ -66,8 +66,7 @@ func New(scope string, rootUID, rootGID int) (*Root, error) {
 		scope:   scope,
 		path:    rootDirectory,
 		volumes: make(map[string]*localVolume),
-		rootUID: rootUID,
-		rootGID: rootGID,
+		rootIDs: rootIDs,
 	}
 
 	dirs, err := ioutil.ReadDir(rootDirectory)
@@ -125,8 +124,7 @@ type Root struct {
 	scope   string
 	path    string
 	volumes map[string]*localVolume
-	rootUID int
-	rootGID int
+	rootIDs idtools.IDPair
 }
 
 // List lists all the volumes
@@ -167,7 +165,7 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error
 	}
 
 	path := r.DataPath(name)
-	if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil {
+	if err := idtools.MkdirAllAndChown(path, 0755, r.rootIDs); err != nil {
 		if os.IsExist(err) {
 			return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
 		}

+ 10 - 9
volume/local/local_test.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/mount"
 )
 
@@ -40,7 +41,7 @@ func TestRemove(t *testing.T) {
 	}
 	defer os.RemoveAll(rootDir)
 
-	r, err := New(rootDir, 0, 0)
+	r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -82,7 +83,7 @@ func TestInitializeWithVolumes(t *testing.T) {
 	}
 	defer os.RemoveAll(rootDir)
 
-	r, err := New(rootDir, 0, 0)
+	r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -92,7 +93,7 @@ func TestInitializeWithVolumes(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	r, err = New(rootDir, 0, 0)
+	r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -114,7 +115,7 @@ func TestCreate(t *testing.T) {
 	}
 	defer os.RemoveAll(rootDir)
 
-	r, err := New(rootDir, 0, 0)
+	r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -151,7 +152,7 @@ func TestCreate(t *testing.T) {
 		}
 	}
 
-	r, err = New(rootDir, 0, 0)
+	r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -189,7 +190,7 @@ func TestCreateWithOpts(t *testing.T) {
 	}
 	defer os.RemoveAll(rootDir)
 
-	r, err := New(rootDir, 0, 0)
+	r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -270,7 +271,7 @@ func TestCreateWithOpts(t *testing.T) {
 		t.Fatal("expected mount to still be active")
 	}
 
-	r, err = New(rootDir, 0, 0)
+	r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -292,7 +293,7 @@ func TestRealodNoOpts(t *testing.T) {
 	}
 	defer os.RemoveAll(rootDir)
 
-	r, err := New(rootDir, 0, 0)
+	r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -320,7 +321,7 @@ func TestRealodNoOpts(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	r, err = New(rootDir, 0, 0)
+	r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
 	if err != nil {
 		t.Fatal(err)
 	}

+ 2 - 2
volume/volume.go

@@ -151,7 +151,7 @@ func (m *MountPoint) Cleanup() error {
 // configured, or creating the source directory if supplied.
 // The, optional, checkFun parameter allows doing additional checking
 // before creating the source directory on the host.
-func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int, checkFun func(m *MountPoint) error) (path string, err error) {
+func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) {
 	defer func() {
 		if err == nil {
 			if label.RelabelNeeded(m.Mode) {
@@ -196,7 +196,7 @@ func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int, checkFun fun
 		}
 		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
 		// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
-		if err := idtools.MkdirAllNewAs(m.Source, 0755, rootUID, rootGID); err != nil {
+		if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil {
 			if perr, ok := err.(*os.PathError); ok {
 				if perr.Err != syscall.ENOTDIR {
 					return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)