Forráskód Böngészése

Merge pull request #35521 from salah-khan/35507

Add --chown flag support for ADD/COPY commands for Windows
John Stephens 6 éve
szülő
commit
b3e9f7b13b
64 módosított fájl, 610 hozzáadás és 301 törlés
  1. 15 15
      builder/dockerfile/builder.go
  2. 8 8
      builder/dockerfile/copy.go
  3. 2 2
      builder/dockerfile/copy_unix.go
  4. 70 4
      builder/dockerfile/copy_windows.go
  5. 14 10
      builder/dockerfile/internals.go
  6. 8 8
      builder/dockerfile/internals_linux.go
  7. 38 13
      builder/dockerfile/internals_linux_test.go
  8. 116 3
      builder/dockerfile/internals_windows.go
  9. 1 1
      cmd/dockerd/daemon.go
  10. 2 2
      container/container.go
  11. 2 2
      daemon/archive_tarcopyoptions.go
  12. 3 1
      daemon/archive_tarcopyoptions_unix.go
  13. 5 5
      daemon/container_operations_unix.go
  14. 3 2
      daemon/create.go
  15. 1 1
      daemon/create_unix.go
  16. 12 12
      daemon/daemon.go
  17. 1 1
      daemon/daemon_linux_test.go
  18. 1 1
      daemon/daemon_test.go
  19. 10 10
      daemon/daemon_unix.go
  20. 4 4
      daemon/daemon_windows.go
  21. 2 2
      daemon/export.go
  22. 3 3
      daemon/graphdriver/aufs/aufs.go
  23. 3 3
      daemon/graphdriver/btrfs/btrfs.go
  24. 2 2
      daemon/graphdriver/devmapper/deviceset.go
  25. 3 3
      daemon/graphdriver/devmapper/driver.go
  26. 3 3
      daemon/graphdriver/lcow/lcow.go
  27. 3 3
      daemon/graphdriver/overlay/overlay.go
  28. 3 3
      daemon/graphdriver/overlay2/overlay.go
  29. 6 6
      daemon/graphdriver/vfs/driver.go
  30. 1 1
      daemon/graphdriver/windows/windows.go
  31. 2 2
      daemon/graphdriver/zfs/zfs.go
  32. 1 1
      daemon/info.go
  33. 4 4
      daemon/initlayer/setup_unix.go
  34. 1 1
      daemon/initlayer/setup_windows.go
  35. 4 4
      daemon/oci_linux.go
  36. 2 2
      daemon/oci_linux_test.go
  37. 2 2
      daemon/volumes_unix.go
  38. 1 1
      daemon/volumes_windows.go
  39. 1 1
      daemon/workdir.go
  40. 20 0
      hack/make/containerutility
  41. 2 0
      hack/make/cross
  42. 6 0
      hack/make/cross-platform-dependent
  43. 3 3
      layer/layer_store.go
  44. 2 2
      libcontainerd/client_daemon_linux.go
  45. 32 34
      pkg/archive/archive.go
  46. 5 5
      pkg/archive/archive_test.go
  47. 3 3
      pkg/archive/archive_unix.go
  48. 2 2
      pkg/archive/archive_windows.go
  49. 2 2
      pkg/archive/diff.go
  50. 7 7
      pkg/chrootarchive/archive.go
  51. 19 17
      pkg/containerfs/archiver.go
  52. 23 22
      pkg/idtools/idtools.go
  53. 5 4
      pkg/idtools/idtools_unix.go
  54. 14 14
      pkg/idtools/idtools_unix_test.go
  55. 6 4
      pkg/idtools/idtools_windows.go
  56. 69 3
      pkg/system/syscall_windows.go
  57. 1 1
      plugin/manager_linux.go
  58. 12 12
      volume/local/local.go
  59. 9 9
      volume/local/local_test.go
  60. 1 1
      volume/mounts/mounts.go
  61. 1 1
      volume/service/default_driver.go
  62. 1 1
      volume/service/default_driver_stubs.go
  63. 1 1
      volume/service/service.go
  64. 1 1
      volume/service/service_linux_test.go

+ 15 - 15
builder/dockerfile/builder.go

@@ -56,21 +56,21 @@ type SessionGetter interface {
 
 
 // BuildManager is shared across all Builder objects
 // BuildManager is shared across all Builder objects
 type BuildManager struct {
 type BuildManager struct {
-	idMappings *idtools.IDMappings
-	backend    builder.Backend
-	pathCache  pathCache // TODO: make this persistent
-	sg         SessionGetter
-	fsCache    *fscache.FSCache
+	idMapping *idtools.IdentityMapping
+	backend   builder.Backend
+	pathCache pathCache // TODO: make this persistent
+	sg        SessionGetter
+	fsCache   *fscache.FSCache
 }
 }
 
 
 // NewBuildManager creates a BuildManager
 // NewBuildManager creates a BuildManager
-func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, idMappings *idtools.IDMappings) (*BuildManager, error) {
+func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, identityMapping *idtools.IdentityMapping) (*BuildManager, error) {
 	bm := &BuildManager{
 	bm := &BuildManager{
-		backend:    b,
-		pathCache:  &syncmap.Map{},
-		sg:         sg,
-		idMappings: idMappings,
-		fsCache:    fsCache,
+		backend:   b,
+		pathCache: &syncmap.Map{},
+		sg:        sg,
+		idMapping: identityMapping,
+		fsCache:   fsCache,
 	}
 	}
 	if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil {
 	if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil {
 		return nil, err
 		return nil, err
@@ -111,7 +111,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
 		ProgressWriter: config.ProgressWriter,
 		ProgressWriter: config.ProgressWriter,
 		Backend:        bm.backend,
 		Backend:        bm.backend,
 		PathCache:      bm.pathCache,
 		PathCache:      bm.pathCache,
-		IDMappings:     bm.idMappings,
+		IDMapping:      bm.idMapping,
 	}
 	}
 	b, err := newBuilder(ctx, builderOptions)
 	b, err := newBuilder(ctx, builderOptions)
 	if err != nil {
 	if err != nil {
@@ -159,7 +159,7 @@ type builderOptions struct {
 	Backend        builder.Backend
 	Backend        builder.Backend
 	ProgressWriter backend.ProgressWriter
 	ProgressWriter backend.ProgressWriter
 	PathCache      pathCache
 	PathCache      pathCache
-	IDMappings     *idtools.IDMappings
+	IDMapping      *idtools.IdentityMapping
 }
 }
 
 
 // Builder is a Dockerfile builder
 // Builder is a Dockerfile builder
@@ -175,7 +175,7 @@ type Builder struct {
 	docker    builder.Backend
 	docker    builder.Backend
 	clientCtx context.Context
 	clientCtx context.Context
 
 
-	idMappings       *idtools.IDMappings
+	idMapping        *idtools.IdentityMapping
 	disableCommit    bool
 	disableCommit    bool
 	imageSources     *imageSources
 	imageSources     *imageSources
 	pathCache        pathCache
 	pathCache        pathCache
@@ -199,7 +199,7 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er
 		Aux:              options.ProgressWriter.AuxFormatter,
 		Aux:              options.ProgressWriter.AuxFormatter,
 		Output:           options.ProgressWriter.Output,
 		Output:           options.ProgressWriter.Output,
 		docker:           options.Backend,
 		docker:           options.Backend,
-		idMappings:       options.IDMappings,
+		idMapping:        options.IDMapping,
 		imageSources:     newImageSources(clientCtx, options),
 		imageSources:     newImageSources(clientCtx, options),
 		pathCache:        options.PathCache,
 		pathCache:        options.PathCache,
 		imageProber:      newImageProber(options.Backend, config.CacheFrom, config.NoCache),
 		imageProber:      newImageProber(options.Backend, config.CacheFrom, config.NoCache),

+ 8 - 8
builder/dockerfile/copy.go

@@ -451,7 +451,7 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
 
 
 type copyFileOptions struct {
 type copyFileOptions struct {
 	decompress bool
 	decompress bool
-	chownPair  idtools.IDPair
+	identity   idtools.Identity
 	archiver   Archiver
 	archiver   Archiver
 }
 }
 
 
@@ -481,7 +481,7 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
 		return errors.Wrapf(err, "source path not found")
 		return errors.Wrapf(err, "source path not found")
 	}
 	}
 	if src.IsDir() {
 	if src.IsDir() {
-		return copyDirectory(archiver, srcEndpoint, destEndpoint, options.chownPair)
+		return copyDirectory(archiver, srcEndpoint, destEndpoint, options.identity)
 	}
 	}
 	if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
 	if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
 		return archiver.UntarPath(srcPath, destPath)
 		return archiver.UntarPath(srcPath, destPath)
@@ -499,7 +499,7 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
 		destPath = dest.root.Join(destPath, source.root.Base(source.path))
 		destPath = dest.root.Join(destPath, source.root.Base(source.path))
 		destEndpoint = &copyEndpoint{driver: dest.root, path: destPath}
 		destEndpoint = &copyEndpoint{driver: dest.root, path: destPath}
 	}
 	}
-	return copyFile(archiver, srcEndpoint, destEndpoint, options.chownPair)
+	return copyFile(archiver, srcEndpoint, destEndpoint, options.identity)
 }
 }
 
 
 func isArchivePath(driver containerfs.ContainerFS, path string) bool {
 func isArchivePath(driver containerfs.ContainerFS, path string) bool {
@@ -517,7 +517,7 @@ func isArchivePath(driver containerfs.ContainerFS, path string) bool {
 	return err == nil
 	return err == nil
 }
 }
 
 
-func copyDirectory(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
+func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error {
 	destExists, err := isExistingDirectory(dest)
 	destExists, err := isExistingDirectory(dest)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "failed to query destination path")
 		return errors.Wrapf(err, "failed to query destination path")
@@ -527,17 +527,17 @@ func copyDirectory(archiver Archiver, source, dest *copyEndpoint, chownPair idto
 		return errors.Wrapf(err, "failed to copy directory")
 		return errors.Wrapf(err, "failed to copy directory")
 	}
 	}
 	// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
 	// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
-	return fixPermissions(source.path, dest.path, chownPair, !destExists)
+	return fixPermissions(source.path, dest.path, identity, !destExists)
 }
 }
 
 
-func copyFile(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
+func copyFile(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error {
 	if runtime.GOOS == "windows" && dest.driver.OS() == "linux" {
 	if runtime.GOOS == "windows" && dest.driver.OS() == "linux" {
 		// LCOW
 		// LCOW
 		if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil {
 		if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil {
 			return errors.Wrapf(err, "failed to create new directory")
 			return errors.Wrapf(err, "failed to create new directory")
 		}
 		}
 	} else {
 	} else {
-		if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, chownPair); err != nil {
+		if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, identity); err != nil {
 			// Normal containers
 			// Normal containers
 			return errors.Wrapf(err, "failed to create new directory")
 			return errors.Wrapf(err, "failed to create new directory")
 		}
 		}
@@ -547,7 +547,7 @@ func copyFile(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.I
 		return errors.Wrapf(err, "failed to copy file")
 		return errors.Wrapf(err, "failed to copy file")
 	}
 	}
 	// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
 	// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
-	return fixPermissions(source.path, dest.path, chownPair, false)
+	return fixPermissions(source.path, dest.path, identity, false)
 }
 }
 
 
 func endsInSlash(driver containerfs.Driver, path string) bool {
 func endsInSlash(driver containerfs.Driver, path string) bool {

+ 2 - 2
builder/dockerfile/copy_unix.go

@@ -10,7 +10,7 @@ import (
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
 )
 )
 
 
-func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
+func fixPermissions(source, destination string, identity idtools.Identity, overrideSkip bool) error {
 	var (
 	var (
 		skipChownRoot bool
 		skipChownRoot bool
 		err           error
 		err           error
@@ -39,7 +39,7 @@ func fixPermissions(source, destination string, rootIDs idtools.IDPair, override
 		}
 		}
 
 
 		fullpath = filepath.Join(destination, cleaned)
 		fullpath = filepath.Join(destination, cleaned)
-		return os.Lchown(fullpath, rootIDs.UID, rootIDs.GID)
+		return os.Lchown(fullpath, identity.UID, identity.GID)
 	})
 	})
 }
 }
 
 

+ 70 - 4
builder/dockerfile/copy_windows.go

@@ -1,11 +1,17 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
-	"errors"
+	"fmt"
+	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
 
 
+	"github.com/Microsoft/go-winio"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/docker/pkg/system"
+	"github.com/pkg/errors"
+	"golang.org/x/sys/windows"
 )
 )
 
 
 var pathBlacklist = map[string]bool{
 var pathBlacklist = map[string]bool{
@@ -13,9 +19,69 @@ var pathBlacklist = map[string]bool{
 	"c:\\windows": true,
 	"c:\\windows": true,
 }
 }
 
 
-func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
-	// chown is not supported on Windows
-	return nil
+func init() {
+	reexec.Register("windows-fix-permissions", fixPermissionsReexec)
+}
+
+func fixPermissions(source, destination string, identity idtools.Identity, _ bool) error {
+	if identity.SID == "" {
+		return nil
+	}
+
+	cmd := reexec.Command("windows-fix-permissions", source, destination, identity.SID)
+	output, err := cmd.CombinedOutput()
+
+	return errors.Wrapf(err, "failed to exec windows-fix-permissions: %s", output)
+}
+
+func fixPermissionsReexec() {
+	err := fixPermissionsWindows(os.Args[1], os.Args[2], os.Args[3])
+	if err != nil {
+		fmt.Fprint(os.Stderr, err)
+		os.Exit(1)
+	}
+}
+
+func fixPermissionsWindows(source, destination, SID string) error {
+
+	privileges := []string{winio.SeRestorePrivilege, system.SeTakeOwnershipPrivilege}
+
+	err := winio.EnableProcessPrivileges(privileges)
+	if err != nil {
+		return err
+	}
+
+	defer winio.DisableProcessPrivileges(privileges)
+
+	sid, err := windows.StringToSid(SID)
+	if err != nil {
+		return err
+	}
+
+	// Owners on *nix have read/write/delete/read control and write DAC.
+	// Add an ACE that grants this to the user/group specified with the
+	// chown option. Currently Windows is not honoring the owner change,
+	// however, they are aware of this and it should be fixed at some
+	// point.
+
+	sddlString := system.SddlAdministratorsLocalSystem
+	sddlString += "(A;OICI;GRGWGXRCWDSD;;;" + SID + ")"
+
+	securityDescriptor, err := winio.SddlToSecurityDescriptor(sddlString)
+	if err != nil {
+		return err
+	}
+
+	var daclPresent uint32
+	var daclDefaulted uint32
+	var dacl *byte
+
+	err = system.GetSecurityDescriptorDacl(&securityDescriptor[0], &daclPresent, &dacl, &daclDefaulted)
+	if err != nil {
+		return err
+	}
+
+	return system.SetNamedSecurityInfo(windows.StringToUTF16Ptr(destination), system.SE_FILE_OBJECT, system.OWNER_SECURITY_INFORMATION|system.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil)
 }
 }
 
 
 func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {
 func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {

+ 14 - 10
builder/dockerfile/internals.go

@@ -37,7 +37,7 @@ type Archiver interface {
 	UntarPath(src, dst string) error
 	UntarPath(src, dst string) error
 	CopyWithTar(src, dst string) error
 	CopyWithTar(src, dst string) error
 	CopyFileWithTar(src, dst string) error
 	CopyFileWithTar(src, dst string) error
-	IDMappings() *idtools.IDMappings
+	IdentityMapping() *idtools.IdentityMapping
 }
 }
 
 
 // The builder will use the following interfaces if the container fs implements
 // The builder will use the following interfaces if the container fs implements
@@ -68,11 +68,11 @@ func tarFunc(i interface{}) containerfs.TarFunc {
 func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
 func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
 	t, u := tarFunc(src), untarFunc(dst)
 	t, u := tarFunc(src), untarFunc(dst)
 	return &containerfs.Archiver{
 	return &containerfs.Archiver{
-		SrcDriver:     src,
-		DstDriver:     dst,
-		Tar:           t,
-		Untar:         u,
-		IDMappingsVar: b.idMappings,
+		SrcDriver: src,
+		DstDriver: dst,
+		Tar:       t,
+		Untar:     u,
+		IDMapping: b.idMapping,
 	}
 	}
 }
 }
 
 
@@ -185,14 +185,18 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
 		return err
 		return err
 	}
 	}
 
 
-	chownPair := b.idMappings.RootPair()
+	identity := b.idMapping.RootPair()
 	// if a chown was requested, perform the steps to get the uid, gid
 	// if a chown was requested, perform the steps to get the uid, gid
 	// translated (if necessary because of user namespaces), and replace
 	// translated (if necessary because of user namespaces), and replace
 	// the root pair with the chown pair for copy operations
 	// the root pair with the chown pair for copy operations
 	if inst.chownStr != "" {
 	if inst.chownStr != "" {
-		chownPair, err = parseChownFlag(inst.chownStr, destInfo.root.Path(), b.idMappings)
+		identity, err = parseChownFlag(b, state, inst.chownStr, destInfo.root.Path(), b.idMapping)
 		if err != nil {
 		if err != nil {
-			return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
+			if b.options.Platform != "windows" {
+				return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
+			}
+
+			return errors.Wrapf(err, "unable to map container user account name to SID")
 		}
 		}
 	}
 	}
 
 
@@ -200,7 +204,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
 		opts := copyFileOptions{
 		opts := copyFileOptions{
 			decompress: inst.allowLocalDecompression,
 			decompress: inst.allowLocalDecompression,
 			archiver:   b.getArchiver(info.root, destInfo.root),
 			archiver:   b.getArchiver(info.root, destInfo.root),
-			chownPair:  chownPair,
+			identity:   identity,
 		}
 		}
 		if err := performCopyForInfo(destInfo, info, opts); err != nil {
 		if err := performCopyForInfo(destInfo, info, opts); err != nil {
 			return errors.Wrapf(err, "failed to copy files")
 			return errors.Wrapf(err, "failed to copy files")

+ 8 - 8
builder/dockerfile/internals_linux.go

@@ -11,11 +11,11 @@ import (
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
-func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
+func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) {
 	var userStr, grpStr string
 	var userStr, grpStr string
 	parts := strings.Split(chown, ":")
 	parts := strings.Split(chown, ":")
 	if len(parts) > 2 {
 	if len(parts) > 2 {
-		return idtools.IDPair{}, errors.New("invalid chown string format: " + chown)
+		return idtools.Identity{}, errors.New("invalid chown string format: " + chown)
 	}
 	}
 	if len(parts) == 1 {
 	if len(parts) == 1 {
 		// if no group specified, use the user spec as group as well
 		// if no group specified, use the user spec as group as well
@@ -26,25 +26,25 @@ func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (
 
 
 	passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
 	passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
 	if err != nil {
 	if err != nil {
-		return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
+		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)
 	groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
 	if err != nil {
 	if err != nil {
-		return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
+		return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
 	}
 	}
 	uid, err := lookupUser(userStr, passwdPath)
 	uid, err := lookupUser(userStr, passwdPath)
 	if err != nil {
 	if err != nil {
-		return idtools.IDPair{}, errors.Wrapf(err, "can't find uid for user "+userStr)
+		return idtools.Identity{}, errors.Wrapf(err, "can't find uid for user "+userStr)
 	}
 	}
 	gid, err := lookupGroup(grpStr, groupPath)
 	gid, err := lookupGroup(grpStr, groupPath)
 	if err != nil {
 	if err != nil {
-		return idtools.IDPair{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
+		return idtools.Identity{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
 	}
 	}
 
 
 	// convert as necessary because of user namespaces
 	// convert as necessary because of user namespaces
-	chownPair, err := idMappings.ToHost(idtools.IDPair{UID: uid, GID: gid})
+	chownPair, err := identityMapping.ToHost(idtools.Identity{UID: uid, GID: gid})
 	if err != nil {
 	if err != nil {
-		return idtools.IDPair{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
+		return idtools.Identity{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
 	}
 	}
 	return chownPair, nil
 	return chownPair, nil
 }
 }

+ 38 - 13
builder/dockerfile/internals_linux_test.go

@@ -5,6 +5,7 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
@@ -34,7 +35,7 @@ othergrp:x:6666:
 		},
 		},
 	}
 	}
 	remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
 	remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
-	unmapped := &idtools.IDMappings{}
+	unmapped := &idtools.IdentityMapping{}
 
 
 	contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
 	contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
 	defer cleanup()
 	defer cleanup()
@@ -49,56 +50,72 @@ othergrp:x:6666:
 
 
 	// positive tests
 	// positive tests
 	for _, testcase := range []struct {
 	for _, testcase := range []struct {
+		builder   *Builder
 		name      string
 		name      string
 		chownStr  string
 		chownStr  string
-		idMapping *idtools.IDMappings
-		expected  idtools.IDPair
+		idMapping *idtools.IdentityMapping
+		state     *dispatchState
+		expected  idtools.Identity
 	}{
 	}{
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UIDNoMap",
 			name:      "UIDNoMap",
 			chownStr:  "1",
 			chownStr:  "1",
 			idMapping: unmapped,
 			idMapping: unmapped,
-			expected:  idtools.IDPair{UID: 1, GID: 1},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 1, GID: 1},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UIDGIDNoMap",
 			name:      "UIDGIDNoMap",
 			chownStr:  "0:1",
 			chownStr:  "0:1",
 			idMapping: unmapped,
 			idMapping: unmapped,
-			expected:  idtools.IDPair{UID: 0, GID: 1},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 0, GID: 1},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UIDWithMap",
 			name:      "UIDWithMap",
 			chownStr:  "0",
 			chownStr:  "0",
 			idMapping: remapped,
 			idMapping: remapped,
-			expected:  idtools.IDPair{UID: 100000, GID: 100000},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 100000, GID: 100000},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UIDGIDWithMap",
 			name:      "UIDGIDWithMap",
 			chownStr:  "1:33",
 			chownStr:  "1:33",
 			idMapping: remapped,
 			idMapping: remapped,
-			expected:  idtools.IDPair{UID: 100001, GID: 100033},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 100001, GID: 100033},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UserNoMap",
 			name:      "UserNoMap",
 			chownStr:  "bin:5555",
 			chownStr:  "bin:5555",
 			idMapping: unmapped,
 			idMapping: unmapped,
-			expected:  idtools.IDPair{UID: 1, GID: 5555},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 1, GID: 5555},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "GroupWithMap",
 			name:      "GroupWithMap",
 			chownStr:  "0:unicorn",
 			chownStr:  "0:unicorn",
 			idMapping: remapped,
 			idMapping: remapped,
-			expected:  idtools.IDPair{UID: 100000, GID: 101002},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 100000, GID: 101002},
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UserOnlyWithMap",
 			name:      "UserOnlyWithMap",
 			chownStr:  "unicorn",
 			chownStr:  "unicorn",
 			idMapping: remapped,
 			idMapping: remapped,
-			expected:  idtools.IDPair{UID: 101001, GID: 101002},
+			state:     &dispatchState{},
+			expected:  idtools.Identity{UID: 101001, GID: 101002},
 		},
 		},
 	} {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 		t.Run(testcase.name, func(t *testing.T) {
-			idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
+			idPair, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
 			assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
 			assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
 			assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
 			assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
 		})
 		})
@@ -106,32 +123,40 @@ othergrp:x:6666:
 
 
 	// error tests
 	// error tests
 	for _, testcase := range []struct {
 	for _, testcase := range []struct {
+		builder   *Builder
 		name      string
 		name      string
 		chownStr  string
 		chownStr  string
-		idMapping *idtools.IDMappings
+		idMapping *idtools.IdentityMapping
+		state     *dispatchState
 		descr     string
 		descr     string
 	}{
 	}{
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "BadChownFlagFormat",
 			name:      "BadChownFlagFormat",
 			chownStr:  "bob:1:555",
 			chownStr:  "bob:1:555",
 			idMapping: unmapped,
 			idMapping: unmapped,
+			state:     &dispatchState{},
 			descr:     "invalid chown string format: bob:1:555",
 			descr:     "invalid chown string format: bob:1:555",
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "UserNoExist",
 			name:      "UserNoExist",
 			chownStr:  "bob",
 			chownStr:  "bob",
 			idMapping: unmapped,
 			idMapping: unmapped,
+			state:     &dispatchState{},
 			descr:     "can't find uid for user bob: no such user: bob",
 			descr:     "can't find uid for user bob: no such user: bob",
 		},
 		},
 		{
 		{
+			builder:   &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
 			name:      "GroupNoExist",
 			name:      "GroupNoExist",
 			chownStr:  "root:bob",
 			chownStr:  "root:bob",
 			idMapping: unmapped,
 			idMapping: unmapped,
+			state:     &dispatchState{},
 			descr:     "can't find gid for group bob: no such group: bob",
 			descr:     "can't find gid for group bob: no such group: bob",
 		},
 		},
 	} {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 		t.Run(testcase.name, func(t *testing.T) {
-			_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
+			_, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
 			assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
 			assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
 		})
 		})
 	}
 	}

+ 116 - 3
builder/dockerfile/internals_windows.go

@@ -1,7 +1,120 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
-import "github.com/docker/docker/pkg/idtools"
+import (
+	"bytes"
+	"os"
+	"path/filepath"
+	"strings"
 
 
-func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
-	return idMappings.RootPair(), nil
+	"github.com/containerd/containerd/platforms"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/mount"
+	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/pkg/system"
+	"github.com/pkg/errors"
+	"golang.org/x/sys/windows"
+)
+
+func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) {
+	if builder.options.Platform == "windows" {
+		return getAccountIdentity(builder, chown, ctrRootPath, state)
+	}
+
+	return identityMapping.RootPair(), nil
+}
+
+func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
+	// If this is potentially a string SID then attempt to convert it to verify
+	// this, otherwise continue looking for the account.
+	if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
+		sid, err := windows.StringToSid(accountName)
+
+		if err == nil {
+			accountSid, err := sid.String()
+
+			if err != nil {
+				return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string")
+			}
+
+			return idtools.Identity{SID: accountSid}, nil
+		}
+	}
+
+	// Attempt to obtain the SID using the name.
+	sid, _, accType, err := windows.LookupSID("", accountName)
+
+	// If this is a SID that is built-in and hence the same across all systems then use that.
+	if err == nil && (accType == windows.SidTypeAlias || accType == windows.SidTypeWellKnownGroup) {
+		accountSid, err := sid.String()
+
+		if err != nil {
+			return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string")
+		}
+
+		return idtools.Identity{SID: accountSid}, nil
+	}
+
+	// Check if the account name is one unique to containers.
+	if strings.EqualFold(accountName, "ContainerAdministrator") {
+		return idtools.Identity{SID: system.ContainerAdministratorSidString}, nil
+
+	} else if strings.EqualFold(accountName, "ContainerUser") {
+		return idtools.Identity{SID: system.ContainerUserSidString}, nil
+	}
+
+	// All other lookups failed, so therefore determine if the account in
+	// question exists in the container and if so, obtain its SID.
+	return lookupNTAccount(builder, accountName, state)
+}
+
+func lookupNTAccount(builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
+
+	source, _ := filepath.Split(os.Args[0])
+
+	target := "C:\\Docker"
+	targetExecutable := target + "\\containerutility.exe"
+
+	optionsPlatform, err := platforms.Parse(builder.options.Platform)
+	if err != nil {
+		return idtools.Identity{}, err
+	}
+
+	runConfig := copyRunConfig(state.runConfig,
+		withCmdCommentString("internal run to obtain NT account information.", optionsPlatform.OS))
+
+	runConfig.ArgsEscaped = true
+	runConfig.Cmd = []string{targetExecutable, "getaccountsid", accountName}
+
+	hostConfig := &container.HostConfig{Mounts: []mount.Mount{
+		{
+			Type:     mount.TypeBind,
+			Source:   source,
+			Target:   target,
+			ReadOnly: true,
+		},
+	},
+	}
+
+	container, err := builder.containerManager.Create(runConfig, hostConfig)
+	if err != nil {
+		return idtools.Identity{}, err
+	}
+
+	stdout := new(bytes.Buffer)
+	stderr := new(bytes.Buffer)
+
+	if err := builder.containerManager.Run(builder.clientCtx, container.ID, stdout, stderr); err != nil {
+		if err, ok := err.(*statusCodeError); ok {
+			return idtools.Identity{}, &jsonmessage.JSONError{
+				Message: stderr.String(),
+				Code:    err.StatusCode(),
+			}
+		}
+		return idtools.Identity{}, err
+	}
+
+	accountSid := stdout.String()
+
+	return idtools.Identity{SID: accountSid}, nil
 }
 }

+ 1 - 1
cmd/dockerd/daemon.go

@@ -279,7 +279,7 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
 		return opts, errors.Wrap(err, "failed to create fscache")
 		return opts, errors.Wrap(err, "failed to create fscache")
 	}
 	}
 
 
-	manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IDMappings())
+	manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IdentityMapping())
 	if err != nil {
 	if err != nil {
 		return opts, err
 		return opts, err
 	}
 	}

+ 2 - 2
container/container.go

@@ -254,7 +254,7 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error
 }
 }
 
 
 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
-func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error {
+func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error {
 	// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
 	// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
 	// We will need to do remote filesystem operations here.
 	// We will need to do remote filesystem operations here.
 	if container.OS != runtime.GOOS {
 	if container.OS != runtime.GOOS {
@@ -271,7 +271,7 @@ func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error
 		return err
 		return err
 	}
 	}
 
 
-	if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIDs); err != nil {
+	if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIdentity); err != nil {
 		pthInfo, err2 := os.Stat(pth)
 		pthInfo, err2 := os.Stat(pth)
 		if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
 		if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
 			return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
 			return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)

+ 2 - 2
daemon/archive_tarcopyoptions.go

@@ -9,7 +9,7 @@ import (
 func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
 func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
 	return &archive.TarOptions{
 	return &archive.TarOptions{
 		NoOverwriteDirNonDir: noOverwriteDirNonDir,
 		NoOverwriteDirNonDir: noOverwriteDirNonDir,
-		UIDMaps:              daemon.idMappings.UIDs(),
-		GIDMaps:              daemon.idMappings.GIDs(),
+		UIDMaps:              daemon.idMapping.UIDs(),
+		GIDMaps:              daemon.idMapping.GIDs(),
 	}
 	}
 }
 }

+ 3 - 1
daemon/archive_tarcopyoptions_unix.go

@@ -18,8 +18,10 @@ func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwrite
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	identity := idtools.Identity{UID: user.Uid, GID: user.Gid}
+
 	return &archive.TarOptions{
 	return &archive.TarOptions{
 		NoOverwriteDirNonDir: noOverwriteDirNonDir,
 		NoOverwriteDirNonDir: noOverwriteDirNonDir,
-		ChownOpts:            &idtools.IDPair{UID: user.Uid, GID: user.Gid},
+		ChownOpts:            &identity,
 	}, nil
 	}, nil
 }
 }

+ 5 - 5
daemon/container_operations_unix.go

@@ -132,7 +132,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
 		fallthrough
 		fallthrough
 
 
 	case ipcMode.IsShareable():
 	case ipcMode.IsShareable():
-		rootIDs := daemon.idMappings.RootPair()
+		rootIDs := daemon.idMapping.RootPair()
 		if !c.HasMountFor("/dev/shm") {
 		if !c.HasMountFor("/dev/shm") {
 			shmPath, err := c.ShmResourcePath()
 			shmPath, err := c.ShmResourcePath()
 			if err != nil {
 			if err != nil {
@@ -179,7 +179,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 	}
 	}
 
 
 	// retrieve possible remapped range start for root UID, GID
 	// retrieve possible remapped range start for root UID, GID
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
 
 
 	for _, s := range c.SecretReferences {
 	for _, s := range c.SecretReferences {
 		// TODO (ehazlett): use type switch when more are supported
 		// TODO (ehazlett): use type switch when more are supported
@@ -278,7 +278,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
 // In practice this is using a tmpfs mount and is used for both "configs" and "secrets"
 // In practice this is using a tmpfs mount and is used for both "configs" and "secrets"
 func (daemon *Daemon) createSecretsDir(c *container.Container) error {
 func (daemon *Daemon) createSecretsDir(c *container.Container) error {
 	// retrieve possible remapped range start for root UID, GID
 	// retrieve possible remapped range start for root UID, GID
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
 	dir, err := c.SecretMountPath()
 	dir, err := c.SecretMountPath()
 	if err != nil {
 	if err != nil {
 		return errors.Wrap(err, "error getting container secrets dir")
 		return errors.Wrap(err, "error getting container secrets dir")
@@ -304,7 +304,7 @@ func (daemon *Daemon) remountSecretDir(c *container.Container) error {
 	if err := label.Relabel(dir, c.MountLabel, false); err != nil {
 	if err := label.Relabel(dir, c.MountLabel, false); err != nil {
 		logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
 		logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
 	}
 	}
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
 	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
 	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
 
 
 	// remount secrets ro
 	// remount secrets ro
@@ -407,5 +407,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	return idtools.MkdirAllAndChown(p, 0700, daemon.idMappings.RootPair())
+	return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair())
 }
 }

+ 3 - 2
daemon/create.go

@@ -155,13 +155,14 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
 	}
 	}
 
 
 	// Set RWLayer for container after mount labels have been set
 	// Set RWLayer for container after mount labels have been set
-	rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMappings))
+	rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMapping))
 	if err != nil {
 	if err != nil {
 		return nil, errdefs.System(err)
 		return nil, errdefs.System(err)
 	}
 	}
 	container.RWLayer = rwLayer
 	container.RWLayer = rwLayer
 
 
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
+
 	if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
 	if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 1 - 1
daemon/create_unix.go

@@ -25,7 +25,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
 	}
 	}
 	defer daemon.Unmount(container)
 	defer daemon.Unmount(container)
 
 
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
 	if err := container.SetupWorkingDirectory(rootIDs); err != nil {
 	if err := container.SetupWorkingDirectory(rootIDs); err != nil {
 		return err
 		return err
 	}
 	}

+ 12 - 12
daemon/daemon.go

@@ -92,7 +92,7 @@ type Daemon struct {
 	seccompEnabled    bool
 	seccompEnabled    bool
 	apparmorEnabled   bool
 	apparmorEnabled   bool
 	shutdown          bool
 	shutdown          bool
-	idMappings        *idtools.IDMappings
+	idMapping         *idtools.IdentityMapping
 	// TODO: move graphDrivers field to an InfoService
 	// TODO: move graphDrivers field to an InfoService
 	graphDrivers map[string]string // By operating system
 	graphDrivers map[string]string // By operating system
 
 
@@ -605,11 +605,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	idMappings, err := setupRemappedRoot(config)
+	idMapping, err := setupRemappedRoot(config)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	rootIDs := idMappings.RootPair()
+	rootIDs := idMapping.RootPair()
 	if err := setupDaemonProcess(config); err != nil {
 	if err := setupDaemonProcess(config); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -787,7 +787,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 			MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
 			MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
 			GraphDriver:               gd,
 			GraphDriver:               gd,
 			GraphDriverOptions:        config.GraphOptions,
 			GraphDriverOptions:        config.GraphOptions,
-			IDMappings:                idMappings,
+			IDMapping:                 idMapping,
 			PluginGetter:              d.PluginStore,
 			PluginGetter:              d.PluginStore,
 			ExperimentalEnabled:       config.Experimental,
 			ExperimentalEnabled:       config.Experimental,
 			OS:                        operatingSystem,
 			OS:                        operatingSystem,
@@ -894,7 +894,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 
 
 	d.EventsService = events.New()
 	d.EventsService = events.New()
 	d.root = config.Root
 	d.root = config.Root
-	d.idMappings = idMappings
+	d.idMapping = idMapping
 	d.seccompEnabled = sysInfo.Seccomp
 	d.seccompEnabled = sysInfo.Seccomp
 	d.apparmorEnabled = sysInfo.AppArmor
 	d.apparmorEnabled = sysInfo.AppArmor
 
 
@@ -1148,7 +1148,7 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) {
 // prepareTempDir prepares and returns the default directory to use
 // prepareTempDir prepares and returns the default directory to use
 // for temporary files.
 // for temporary files.
 // If it doesn't exist, it is created. If it exists, its content is removed.
 // If it doesn't exist, it is created. If it exists, its content is removed.
-func prepareTempDir(rootDir string, rootIDs idtools.IDPair) (string, error) {
+func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, error) {
 	var tmpDir string
 	var tmpDir string
 	if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
 	if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
 		tmpDir = filepath.Join(rootDir, "tmp")
 		tmpDir = filepath.Join(rootDir, "tmp")
@@ -1168,7 +1168,7 @@ func prepareTempDir(rootDir string, rootIDs idtools.IDPair) (string, error) {
 	}
 	}
 	// We don't remove the content of tmpdir if it's not the default,
 	// We don't remove the content of tmpdir if it's not the default,
 	// it may hold things that do not belong to us.
 	// it may hold things that do not belong to us.
-	return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIDs)
+	return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIdentity)
 }
 }
 
 
 func (daemon *Daemon) setGenericResources(conf *config.Config) error {
 func (daemon *Daemon) setGenericResources(conf *config.Config) error {
@@ -1316,11 +1316,11 @@ func CreateDaemonRoot(config *config.Config) error {
 		}
 		}
 	}
 	}
 
 
-	idMappings, err := setupRemappedRoot(config)
+	idMapping, err := setupRemappedRoot(config)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	return setupDaemonRoot(config, realRoot, idMappings.RootPair())
+	return setupDaemonRoot(config, realRoot, idMapping.RootPair())
 }
 }
 
 
 // checkpointAndSave grabs a container lock to safely call container.CheckpointTo
 // checkpointAndSave grabs a container lock to safely call container.CheckpointTo
@@ -1346,9 +1346,9 @@ func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore {
 	return &daemon.attachmentStore
 	return &daemon.attachmentStore
 }
 }
 
 
-// IDMappings returns uid/gid mappings for the builder
-func (daemon *Daemon) IDMappings() *idtools.IDMappings {
-	return daemon.idMappings
+// IdentityMapping returns uid/gid mapping or a SID (in the case of Windows) for the builder
+func (daemon *Daemon) IdentityMapping() *idtools.IdentityMapping {
+	return daemon.idMapping
 }
 }
 
 
 // ImageService returns the Daemon's ImageService
 // ImageService returns the Daemon's ImageService

+ 1 - 1
daemon/daemon_linux_test.go

@@ -124,7 +124,7 @@ func TestTmpfsDevShmSizeOverride(t *testing.T) {
 	mnt := "/dev/shm"
 	mnt := "/dev/shm"
 
 
 	d := Daemon{
 	d := Daemon{
-		idMappings: &idtools.IDMappings{},
+		idMapping: &idtools.IdentityMapping{},
 	}
 	}
 	c := &container.Container{
 	c := &container.Container{
 		HostConfig: &containertypes.HostConfig{
 		HostConfig: &containertypes.HostConfig{

+ 1 - 1
daemon/daemon_test.go

@@ -118,7 +118,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
 		repository: tmp,
 		repository: tmp,
 		root:       tmp,
 		root:       tmp,
 	}
 	}
-	daemon.volumes, err = volumesservice.NewVolumeService(tmp, nil, idtools.IDPair{UID: 0, GID: 0}, daemon)
+	daemon.volumes, err = volumesservice.NewVolumeService(tmp, nil, idtools.Identity{UID: 0, GID: 0}, daemon)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 10 - 10
daemon/daemon_unix.go

@@ -1003,9 +1003,9 @@ func removeDefaultBridgeInterface() {
 	}
 	}
 }
 }
 
 
-func setupInitLayer(idMappings *idtools.IDMappings) func(containerfs.ContainerFS) error {
+func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error {
 	return func(initPath containerfs.ContainerFS) error {
 	return func(initPath containerfs.ContainerFS) error {
-		return initlayer.Setup(initPath, idMappings.RootPair())
+		return initlayer.Setup(initPath, idMapping.RootPair())
 	}
 	}
 }
 }
 
 
@@ -1102,7 +1102,7 @@ func parseRemappedRoot(usergrp string) (string, string, error) {
 	return username, groupname, nil
 	return username, groupname, nil
 }
 }
 
 
-func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
+func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) {
 	if runtime.GOOS != "linux" && config.RemappedRoot != "" {
 	if runtime.GOOS != "linux" && config.RemappedRoot != "" {
 		return nil, fmt.Errorf("User namespaces are only supported on Linux")
 		return nil, fmt.Errorf("User namespaces are only supported on Linux")
 	}
 	}
@@ -1118,22 +1118,22 @@ func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
 			// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
 			// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
 			// effectively
 			// effectively
 			logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
 			logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
-			return &idtools.IDMappings{}, nil
+			return &idtools.IdentityMapping{}, nil
 		}
 		}
 		logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
 		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
 		// update remapped root setting now that we have resolved them to actual names
 		config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
 		config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
 
 
-		mappings, err := idtools.NewIDMappings(username, groupname)
+		mappings, err := idtools.NewIdentityMapping(username, groupname)
 		if err != nil {
 		if err != nil {
 			return nil, errors.Wrap(err, "Can't create ID mappings")
 			return nil, errors.Wrap(err, "Can't create ID mappings")
 		}
 		}
 		return mappings, nil
 		return mappings, nil
 	}
 	}
-	return &idtools.IDMappings{}, nil
+	return &idtools.IdentityMapping{}, nil
 }
 }
 
 
-func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
+func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error {
 	config.Root = rootDir
 	config.Root = rootDir
 	// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
 	// 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
 	// so that syscalls executing as non-root, operating on subdirectories of the graph root
@@ -1158,10 +1158,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPa
 	// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
 	// 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)
 	// `chdir()` to work for containers namespaced to that uid/gid)
 	if config.RemappedRoot != "" {
 	if config.RemappedRoot != "" {
-		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIDs.UID, rootIDs.GID))
+		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIdentity.UID, rootIdentity.GID))
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
 		// Create the root directory if it doesn't exist
 		// Create the root directory if it doesn't exist
-		if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIDs); err != nil {
+		if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); err != nil {
 			return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
 			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
 		// we also need to verify that any pre-existing directories in the path to
@@ -1174,7 +1174,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPa
 			if dirPath == "/" {
 			if dirPath == "/" {
 				break
 				break
 			}
 			}
-			if !idtools.CanAccess(dirPath, rootIDs) {
+			if !idtools.CanAccess(dirPath, rootIdentity) {
 				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)
 				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)
 			}
 			}
 		}
 		}

+ 4 - 4
daemon/daemon_windows.go

@@ -53,7 +53,7 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos
 	return nil
 	return nil
 }
 }
 
 
-func setupInitLayer(idMappings *idtools.IDMappings) func(containerfs.ContainerFS) error {
+func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -465,11 +465,11 @@ func (daemon *Daemon) cleanupMounts() error {
 	return nil
 	return nil
 }
 }
 
 
-func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
-	return &idtools.IDMappings{}, nil
+func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) {
+	return &idtools.IdentityMapping{}, nil
 }
 }
 
 
-func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
+func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error {
 	config.Root = rootDir
 	config.Root = rootDir
 	// Create the root directory if it doesn't exists
 	// Create the root directory if it doesn't exists
 	if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil {
 	if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil {

+ 2 - 2
daemon/export.go

@@ -68,8 +68,8 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R
 
 
 	archive, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{
 	archive, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{
 		Compression: archive.Uncompressed,
 		Compression: archive.Uncompressed,
-		UIDMaps:     daemon.idMappings.UIDs(),
-		GIDMaps:     daemon.idMappings.GIDs(),
+		UIDMaps:     daemon.idMapping.UIDs(),
+		GIDMaps:     daemon.idMapping.GIDs(),
 	})
 	})
 	if err != nil {
 	if err != nil {
 		rwlayer.Unmount()
 		rwlayer.Unmount()

+ 3 - 3
daemon/graphdriver/aufs/aufs.go

@@ -135,13 +135,13 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		return nil, err
 		return nil, err
 	}
 	}
 	// Create the root aufs driver dir
 	// Create the root aufs driver dir
-	if err := idtools.MkdirAllAndChown(root, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(root, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	// Populate the dir structure
 	// Populate the dir structure
 	for _, p := range paths {
 	for _, p := range paths {
-		if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+		if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 	}
 	}
@@ -289,7 +289,7 @@ func (a *Driver) createDirsFor(id string) error {
 	// The path of directories are <aufs_root_path>/mnt/<image_id>
 	// The path of directories are <aufs_root_path>/mnt/<image_id>
 	// and <aufs_root_path>/diff/<image_id>
 	// and <aufs_root_path>/diff/<image_id>
 	for _, p := range paths {
 	for _, p := range paths {
-		if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+		if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 3 - 3
daemon/graphdriver/btrfs/btrfs.go

@@ -72,7 +72,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -502,7 +502,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return err
 		return err
 	}
 	}
 	if parent == "" {
 	if parent == "" {
@@ -537,7 +537,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
 		if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil {
 		if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil {
 			return err
 			return err
 		}
 		}
-		if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+		if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 			return err
 			return err
 		}
 		}
 		if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {
 		if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {

+ 2 - 2
daemon/graphdriver/devmapper/deviceset.go

@@ -268,7 +268,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil {
+	if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil {
 		return "", err
 		return "", err
 	}
 	}
 
 
@@ -1691,7 +1691,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if err := idtools.MkdirAndChown(devices.root, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil {
+	if err := idtools.MkdirAndChown(devices.root, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil {
 		return err
 		return err
 	}
 	}
 	if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil {
 	if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil {

+ 3 - 3
daemon/graphdriver/devmapper/driver.go

@@ -200,11 +200,11 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
 	}
 	}
 
 
 	// Create the target directories if they don't exist
 	// Create the target directories if they don't exist
-	if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil {
+	if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.Identity{UID: uid, GID: gid}); err != nil {
 		d.ctr.Decrement(mp)
 		d.ctr.Decrement(mp)
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := idtools.MkdirAndChown(mp, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
+	if err := idtools.MkdirAndChown(mp, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
 		d.ctr.Decrement(mp)
 		d.ctr.Decrement(mp)
 		return nil, err
 		return nil, err
 	}
 	}
@@ -215,7 +215,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil {
+	if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil {
 		d.ctr.Decrement(mp)
 		d.ctr.Decrement(mp)
 		d.DeviceSet.UnmountDevice(id, mp)
 		d.DeviceSet.UnmountDevice(id, mp)
 		return nil, err
 		return nil, err

+ 3 - 3
daemon/graphdriver/lcow/lcow.go

@@ -183,17 +183,17 @@ func InitDriver(dataRoot string, options []string, _, _ []idtools.IDMap) (graphd
 	}
 	}
 
 
 	// Make sure the dataRoot directory is created
 	// Make sure the dataRoot directory is created
-	if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
+	if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err)
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err)
 	}
 	}
 
 
 	// Make sure the cache directory is created under dataRoot
 	// Make sure the cache directory is created under dataRoot
-	if err := idtools.MkdirAllAndChown(cd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
+	if err := idtools.MkdirAllAndChown(cd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err)
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err)
 	}
 	}
 
 
 	// Make sure the scratch directory is created under dataRoot
 	// Make sure the scratch directory is created under dataRoot
-	if err := idtools.MkdirAllAndChown(sd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
+	if err := idtools.MkdirAllAndChown(sd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err)
 		return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err)
 	}
 	}
 
 

+ 3 - 3
daemon/graphdriver/overlay/overlay.go

@@ -168,7 +168,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		return nil, err
 		return nil, err
 	}
 	}
 	// Create the driver home dir
 	// Create the driver home dir
-	if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -291,7 +291,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	root := idtools.IDPair{UID: rootUID, GID: rootGID}
+	root := idtools.Identity{UID: rootUID, GID: rootGID}
 
 
 	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
 	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
 		return err
 		return err
@@ -413,7 +413,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	var (
 	var (

+ 3 - 3
daemon/graphdriver/overlay2/overlay.go

@@ -200,7 +200,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 		return nil, err
 		return nil, err
 	}
 	}
 	// Create the driver home dir
 	// Create the driver home dir
-	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -378,7 +378,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	root := idtools.IDPair{UID: rootUID, GID: rootGID}
+	root := idtools.Identity{UID: rootUID, GID: rootGID}
 
 
 	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
 	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
 		return err
 		return err
@@ -586,7 +586,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 

+ 6 - 6
daemon/graphdriver/vfs/driver.go

@@ -27,10 +27,10 @@ func init() {
 // This sets the home directory for the driver and returns NaiveDiffDriver.
 // This sets the home directory for the driver and returns NaiveDiffDriver.
 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
 	d := &Driver{
 	d := &Driver{
-		home:       home,
-		idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
+		home:      home,
+		idMapping: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
 	}
 	}
-	rootIDs := d.idMappings.RootPair()
+	rootIDs := d.idMapping.RootPair()
 	if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
 	if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -46,8 +46,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
 // Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
 // Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
 type Driver struct {
 type Driver struct {
 	driverQuota
 	driverQuota
-	home       string
-	idMappings *idtools.IDMappings
+	home      string
+	idMapping *idtools.IdentityMapping
 }
 }
 
 
 func (d *Driver) String() string {
 func (d *Driver) String() string {
@@ -105,7 +105,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
 
 
 func (d *Driver) create(id, parent string, size uint64) error {
 func (d *Driver) create(id, parent string, size uint64) error {
 	dir := d.dir(id)
 	dir := d.dir(id)
-	rootIDs := d.idMappings.RootPair()
+	rootIDs := d.idMapping.RootPair()
 	if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
 	if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
daemon/graphdriver/windows/windows.go

@@ -95,7 +95,7 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
 		return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
 		return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
 	}
 	}
 
 
-	if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
+	if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
 		return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
 		return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
 	}
 	}
 
 

+ 2 - 2
daemon/graphdriver/zfs/zfs.go

@@ -106,7 +106,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Failed to get root uid/guid: %v", err)
 		return nil, fmt.Errorf("Failed to get root uid/guid: %v", err)
 	}
 	}
-	if err := idtools.MkdirAllAndChown(base, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(base, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
 		return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
 	}
 	}
 
 
@@ -385,7 +385,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
 		return nil, err
 		return nil, err
 	}
 	}
 	// Create the target directories if they don't exist
 	// Create the target directories if they don't exist
-	if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
+	if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 

+ 1 - 1
daemon/info.go

@@ -162,7 +162,7 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
 	if selinuxEnabled() {
 	if selinuxEnabled() {
 		securityOptions = append(securityOptions, "name=selinux")
 		securityOptions = append(securityOptions, "name=selinux")
 	}
 	}
-	if rootIDs := daemon.idMappings.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
+	if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
 		securityOptions = append(securityOptions, "name=userns")
 		securityOptions = append(securityOptions, "name=userns")
 	}
 	}
 	v.SecurityOptions = securityOptions
 	v.SecurityOptions = securityOptions

+ 4 - 4
daemon/initlayer/setup_unix.go

@@ -17,7 +17,7 @@ import (
 //
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // 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.
 // the container from unwanted side-effects on the rw layer.
-func Setup(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) error {
+func Setup(initLayerFs containerfs.ContainerFS, rootIdentity idtools.Identity) error {
 	// Since all paths are local to the container, we can just extract initLayerFs.Path()
 	// Since all paths are local to the container, we can just extract initLayerFs.Path()
 	initLayer := initLayerFs.Path()
 	initLayer := initLayerFs.Path()
 
 
@@ -42,12 +42,12 @@ func Setup(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) error {
 
 
 		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
 		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
 			if os.IsNotExist(err) {
 			if os.IsNotExist(err) {
-				if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIDs); err != nil {
+				if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIdentity); err != nil {
 					return err
 					return err
 				}
 				}
 				switch typ {
 				switch typ {
 				case "dir":
 				case "dir":
-					if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIDs); err != nil {
+					if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIdentity); err != nil {
 						return err
 						return err
 					}
 					}
 				case "file":
 				case "file":
@@ -55,7 +55,7 @@ func Setup(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) error {
 					if err != nil {
 					if err != nil {
 						return err
 						return err
 					}
 					}
-					f.Chown(rootIDs.UID, rootIDs.GID)
+					f.Chown(rootIdentity.UID, rootIdentity.GID)
 					f.Close()
 					f.Close()
 				default:
 				default:
 					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
 					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {

+ 1 - 1
daemon/initlayer/setup_windows.go

@@ -11,6 +11,6 @@ import (
 //
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // 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.
 // the container from unwanted side-effects on the rw layer.
-func Setup(initLayer containerfs.ContainerFS, rootIDs idtools.IDPair) error {
+func Setup(initLayer containerfs.ContainerFS, rootIDs idtools.Identity) error {
 	return nil
 	return nil
 }
 }

+ 4 - 4
daemon/oci_linux.go

@@ -217,13 +217,13 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
 	userNS := false
 	userNS := false
 	// user
 	// user
 	if c.HostConfig.UsernsMode.IsPrivate() {
 	if c.HostConfig.UsernsMode.IsPrivate() {
-		uidMap := daemon.idMappings.UIDs()
+		uidMap := daemon.idMapping.UIDs()
 		if uidMap != nil {
 		if uidMap != nil {
 			userNS = true
 			userNS = true
 			ns := specs.LinuxNamespace{Type: "user"}
 			ns := specs.LinuxNamespace{Type: "user"}
 			setNamespace(s, ns)
 			setNamespace(s, ns)
 			s.Linux.UIDMappings = specMapping(uidMap)
 			s.Linux.UIDMappings = specMapping(uidMap)
-			s.Linux.GIDMappings = specMapping(daemon.idMappings.GIDs())
+			s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs())
 		}
 		}
 	}
 	}
 	// network
 	// network
@@ -619,7 +619,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,
 	// 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)
 	// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
-	if uidMap := daemon.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged {
+	if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged {
 		for i, m := range s.Mounts {
 		for i, m := range s.Mounts {
 			if m.Type == "cgroup" {
 			if m.Type == "cgroup" {
 				clearReadOnly(&s.Mounts[i])
 				clearReadOnly(&s.Mounts[i])
@@ -642,7 +642,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
 		Path:     c.BaseFS.Path(),
 		Path:     c.BaseFS.Path(),
 		Readonly: c.HostConfig.ReadonlyRootfs,
 		Readonly: c.HostConfig.ReadonlyRootfs,
 	}
 	}
-	if err := c.SetupWorkingDirectory(daemon.idMappings.RootPair()); err != nil {
+	if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
 		return err
 		return err
 	}
 	}
 	cwd := c.Config.WorkingDir
 	cwd := c.Config.WorkingDir

+ 2 - 2
daemon/oci_linux_test.go

@@ -21,7 +21,7 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
 	d := Daemon{
 	d := Daemon{
 		// some empty structs to avoid getting a panic
 		// some empty structs to avoid getting a panic
 		// caused by a null pointer dereference
 		// caused by a null pointer dereference
-		idMappings:  &idtools.IDMappings{},
+		idMapping:   &idtools.IdentityMapping{},
 		configStore: &config.Config{},
 		configStore: &config.Config{},
 	}
 	}
 	c := &container.Container{
 	c := &container.Container{
@@ -58,7 +58,7 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
 	d := Daemon{
 	d := Daemon{
 		// some empty structs to avoid getting a panic
 		// some empty structs to avoid getting a panic
 		// caused by a null pointer dereference
 		// caused by a null pointer dereference
-		idMappings:  &idtools.IDMappings{},
+		idMapping:   &idtools.IdentityMapping{},
 		configStore: &config.Config{},
 		configStore: &config.Config{},
 	}
 	}
 	c := &container.Container{
 	c := &container.Container{

+ 2 - 2
daemon/volumes_unix.go

@@ -47,7 +47,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 			return nil
 			return nil
 		}
 		}
 
 
-		path, err := m.Setup(c.MountLabel, daemon.idMappings.RootPair(), checkfunc)
+		path, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -77,7 +77,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 	// if we are going to mount any of the network files from container
 	// if we are going to mount any of the network files from container
 	// metadata, the ownership must be set properly for potential container
 	// metadata, the ownership must be set properly for potential container
 	// remapped root (user namespaces)
 	// remapped root (user namespaces)
-	rootIDs := daemon.idMappings.RootPair()
+	rootIDs := daemon.idMapping.RootPair()
 	for _, mount := range netMounts {
 	for _, mount := range netMounts {
 		// we should only modify ownership of network files within our own container
 		// we should only modify ownership of network files within our own container
 		// metadata repository. If the user specifies a mount path external, it is
 		// metadata repository. If the user specifies a mount path external, it is

+ 1 - 1
daemon/volumes_windows.go

@@ -24,7 +24,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
 		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		s, err := mount.Setup(c.MountLabel, idtools.IDPair{UID: 0, GID: 0}, nil)
+		s, err := mount.Setup(c.MountLabel, idtools.Identity{}, nil)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}

+ 1 - 1
daemon/workdir.go

@@ -16,5 +16,5 @@ func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
 		return err
 		return err
 	}
 	}
 	defer daemon.Unmount(container)
 	defer daemon.Unmount(container)
-	return container.SetupWorkingDirectory(daemon.idMappings.RootPair())
+	return container.SetupWorkingDirectory(daemon.idMapping.RootPair())
 }
 }

+ 20 - 0
hack/make/containerutility

@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+
+CONTAINER_UTILITY_COMMIT=e004a1415a433447369e315b9d7df357102be0d2 # v0.9.0
+
+(
+	git clone https://github.com/docker/windows-container-utility.git "$GOPATH/src/github.com/docker/windows-container-utility"
+	cd "$GOPATH/src/github.com/docker/windows-container-utility"
+	git checkout -q "$CONTAINER_UTILITY_COMMIT"
+
+	echo Building: ${DEST}/containerutility.exe
+
+	(
+		make
+	)
+
+	mkdir -p ${ABS_DEST}
+
+	cp containerutility.exe ${ABS_DEST}/containerutility.exe
+)

+ 2 - 0
hack/make/cross

@@ -25,5 +25,7 @@ for platform in $DOCKER_CROSSPLATFORMS; do
 		mkdir -p "$DEST"
 		mkdir -p "$DEST"
 		ABS_DEST="$(cd "$DEST" && pwd -P)"
 		ABS_DEST="$(cd "$DEST" && pwd -P)"
 		source "${MAKEDIR}/binary-daemon"
 		source "${MAKEDIR}/binary-daemon"
+
+		source "${MAKEDIR}/cross-platform-dependent"
 	)
 	)
 done
 done

+ 6 - 0
hack/make/cross-platform-dependent

@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+
+if [ $platform == "windows/amd64" ]; then
+	source "${MAKEDIR}/containerutility"
+fi

+ 3 - 3
layer/layer_store.go

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

+ 2 - 2
libcontainerd/client_daemon_linux.go

@@ -58,7 +58,7 @@ func getSpecUser(ociSpec *specs.Spec) (int, int) {
 func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
 func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
 	uid, gid := getSpecUser(ociSpec)
 	uid, gid := getSpecUser(ociSpec)
 	if uid == 0 && gid == 0 {
 	if uid == 0 && gid == 0 {
-		return bundleDir, idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.IDPair{UID: 0, GID: 0})
+		return bundleDir, idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0})
 	}
 	}
 
 
 	p := string(filepath.Separator)
 	p := string(filepath.Separator)
@@ -71,7 +71,7 @@ func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
 		}
 		}
 		if os.IsNotExist(err) || fi.Mode()&1 == 0 {
 		if os.IsNotExist(err) || fi.Mode()&1 == 0 {
 			p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
 			p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
-			if err := idtools.MkdirAndChown(p, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
+			if err := idtools.MkdirAndChown(p, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
 				return "", err
 				return "", err
 			}
 			}
 		}
 		}

+ 32 - 34
pkg/archive/archive.go

@@ -52,7 +52,7 @@ type (
 		NoLchown         bool
 		NoLchown         bool
 		UIDMaps          []idtools.IDMap
 		UIDMaps          []idtools.IDMap
 		GIDMaps          []idtools.IDMap
 		GIDMaps          []idtools.IDMap
-		ChownOpts        *idtools.IDPair
+		ChownOpts        *idtools.Identity
 		IncludeSourceDir bool
 		IncludeSourceDir bool
 		// WhiteoutFormat is the expected on disk format for whiteout files.
 		// WhiteoutFormat is the expected on disk format for whiteout files.
 		// This format will be converted to the standard format on pack
 		// This format will be converted to the standard format on pack
@@ -72,13 +72,13 @@ type (
 // this package with a pluggable Untar function. Also, to facilitate the passing of specific id
 // this package with a pluggable Untar function. Also, to facilitate the passing of specific id
 // mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
 // mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
 type Archiver struct {
 type Archiver struct {
-	Untar         func(io.Reader, string, *TarOptions) error
-	IDMappingsVar *idtools.IDMappings
+	Untar     func(io.Reader, string, *TarOptions) error
+	IDMapping *idtools.IdentityMapping
 }
 }
 
 
-// NewDefaultArchiver returns a new Archiver without any IDMappings
+// NewDefaultArchiver returns a new Archiver without any IdentityMapping
 func NewDefaultArchiver() *Archiver {
 func NewDefaultArchiver() *Archiver {
-	return &Archiver{Untar: Untar, IDMappingsVar: &idtools.IDMappings{}}
+	return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}}
 }
 }
 
 
 // breakoutError is used to differentiate errors related to breaking out
 // breakoutError is used to differentiate errors related to breaking out
@@ -420,9 +420,9 @@ type tarAppender struct {
 	Buffer    *bufio.Writer
 	Buffer    *bufio.Writer
 
 
 	// for hardlink mapping
 	// for hardlink mapping
-	SeenFiles  map[uint64]string
-	IDMappings *idtools.IDMappings
-	ChownOpts  *idtools.IDPair
+	SeenFiles       map[uint64]string
+	IdentityMapping *idtools.IdentityMapping
+	ChownOpts       *idtools.Identity
 
 
 	// For packing and unpacking whiteout files in the
 	// For packing and unpacking whiteout files in the
 	// non standard format. The whiteout files defined
 	// non standard format. The whiteout files defined
@@ -431,13 +431,13 @@ type tarAppender struct {
 	WhiteoutConverter tarWhiteoutConverter
 	WhiteoutConverter tarWhiteoutConverter
 }
 }
 
 
-func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer, chownOpts *idtools.IDPair) *tarAppender {
+func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
 	return &tarAppender{
 	return &tarAppender{
-		SeenFiles:  make(map[uint64]string),
-		TarWriter:  tar.NewWriter(writer),
-		Buffer:     pools.BufioWriter32KPool.Get(nil),
-		IDMappings: idMapping,
-		ChownOpts:  chownOpts,
+		SeenFiles:       make(map[uint64]string),
+		TarWriter:       tar.NewWriter(writer),
+		Buffer:          pools.BufioWriter32KPool.Get(nil),
+		IdentityMapping: idMapping,
+		ChownOpts:       chownOpts,
 	}
 	}
 }
 }
 
 
@@ -502,14 +502,12 @@ func (ta *tarAppender) addTarFile(path, name string) error {
 	//handle re-mapping container ID mappings back to host ID mappings before
 	//handle re-mapping container ID mappings back to host ID mappings before
 	//writing tar headers/files. We skip whiteout files because they were written
 	//writing tar headers/files. We skip whiteout files because they were written
 	//by the kernel and already have proper ownership relative to the host
 	//by the kernel and already have proper ownership relative to the host
-	if !isOverlayWhiteout &&
-		!strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) &&
-		!ta.IDMappings.Empty() {
+	if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
 		fileIDPair, err := getFileUIDGID(fi.Sys())
 		fileIDPair, err := getFileUIDGID(fi.Sys())
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		hdr.Uid, hdr.Gid, err = ta.IDMappings.ToContainer(fileIDPair)
+		hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIDPair)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -572,7 +570,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
 	return nil
 	return nil
 }
 }
 
 
-func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.IDPair, inUserns bool) error {
+func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.Identity, inUserns bool) error {
 	// hdr.Mode is in linux format, which we can use for sycalls,
 	// hdr.Mode is in linux format, which we can use for sycalls,
 	// but for os.Foo() calls we need the mode converted to os.FileMode,
 	// but for os.Foo() calls we need the mode converted to os.FileMode,
 	// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
 	// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
@@ -652,7 +650,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
 	// Lchown is not supported on Windows.
 	// Lchown is not supported on Windows.
 	if Lchown && runtime.GOOS != "windows" {
 	if Lchown && runtime.GOOS != "windows" {
 		if chownOpts == nil {
 		if chownOpts == nil {
-			chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
+			chownOpts = &idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}
 		}
 		}
 		if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
 		if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
 			return err
 			return err
@@ -901,8 +899,8 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
 	defer pools.BufioReader32KPool.Put(trBuf)
 	defer pools.BufioReader32KPool.Put(trBuf)
 
 
 	var dirs []*tar.Header
 	var dirs []*tar.Header
-	idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
-	rootIDs := idMappings.RootPair()
+	idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
+	rootIDs := idMapping.RootPair()
 	whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
 	whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
 
 
 	// Iterate through the files in the archive.
 	// Iterate through the files in the archive.
@@ -981,7 +979,7 @@ loop:
 		}
 		}
 		trBuf.Reset(tr)
 		trBuf.Reset(tr)
 
 
-		if err := remapIDs(idMappings, hdr); err != nil {
+		if err := remapIDs(idMapping, hdr); err != nil {
 			return err
 			return err
 		}
 		}
 
 
@@ -1068,8 +1066,8 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
 	}
 	}
 	defer archive.Close()
 	defer archive.Close()
 	options := &TarOptions{
 	options := &TarOptions{
-		UIDMaps: archiver.IDMappingsVar.UIDs(),
-		GIDMaps: archiver.IDMappingsVar.GIDs(),
+		UIDMaps: archiver.IDMapping.UIDs(),
+		GIDMaps: archiver.IDMapping.GIDs(),
 	}
 	}
 	return archiver.Untar(archive, dst, options)
 	return archiver.Untar(archive, dst, options)
 }
 }
@@ -1082,8 +1080,8 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
 	}
 	}
 	defer archive.Close()
 	defer archive.Close()
 	options := &TarOptions{
 	options := &TarOptions{
-		UIDMaps: archiver.IDMappingsVar.UIDs(),
-		GIDMaps: archiver.IDMappingsVar.GIDs(),
+		UIDMaps: archiver.IDMapping.UIDs(),
+		GIDMaps: archiver.IDMapping.GIDs(),
 	}
 	}
 	return archiver.Untar(archive, dst, options)
 	return archiver.Untar(archive, dst, options)
 }
 }
@@ -1104,7 +1102,7 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
 	// if this Archiver is set up with ID mapping we need to create
 	// if this Archiver is set up with ID mapping we need to create
 	// the new destination directory with the remapped root UID/GID pair
 	// the new destination directory with the remapped root UID/GID pair
 	// as owner
 	// as owner
-	rootIDs := archiver.IDMappingsVar.RootPair()
+	rootIDs := archiver.IDMapping.RootPair()
 	// Create dst, copy src's content into it
 	// Create dst, copy src's content into it
 	logrus.Debugf("Creating dest directory: %s", dst)
 	logrus.Debugf("Creating dest directory: %s", dst)
 	if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
 	if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
@@ -1164,7 +1162,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 			hdr.Name = filepath.Base(dst)
 			hdr.Name = filepath.Base(dst)
 			hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
 			hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
 
 
-			if err := remapIDs(archiver.IDMappingsVar, hdr); err != nil {
+			if err := remapIDs(archiver.IDMapping, hdr); err != nil {
 				return err
 				return err
 			}
 			}
 
 
@@ -1192,13 +1190,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 	return err
 	return err
 }
 }
 
 
-// IDMappings returns the IDMappings of the archiver.
-func (archiver *Archiver) IDMappings() *idtools.IDMappings {
-	return archiver.IDMappingsVar
+// IdentityMapping returns the IdentityMapping of the archiver.
+func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
+	return archiver.IDMapping
 }
 }
 
 
-func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
-	ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
+func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
+	ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
 	hdr.Uid, hdr.Gid = ids.UID, ids.GID
 	hdr.Uid, hdr.Gid = ids.UID, ids.GID
 	return err
 	return err
 }
 }

+ 5 - 5
pkg/archive/archive_test.go

@@ -755,11 +755,11 @@ func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) {
 		expectedUID int
 		expectedUID int
 		expectedGID int
 		expectedGID int
 	}{
 	}{
-		{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1337, GID: 42}}, 1337, 42},
-		{&TarOptions{ChownOpts: &idtools.IDPair{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001},
-		{&TarOptions{ChownOpts: &idtools.IDPair{UID: 0, GID: 0}, NoLchown: false}, 0, 0},
-		{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1, GID: 1}, NoLchown: true}, 1, 1},
-		{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000},
+		{&TarOptions{ChownOpts: &idtools.Identity{UID: 1337, GID: 42}}, 1337, 42},
+		{&TarOptions{ChownOpts: &idtools.Identity{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001},
+		{&TarOptions{ChownOpts: &idtools.Identity{UID: 0, GID: 0}, NoLchown: false}, 0, 0},
+		{&TarOptions{ChownOpts: &idtools.Identity{UID: 1, GID: 1}, NoLchown: true}, 1, 1},
+		{&TarOptions{ChownOpts: &idtools.Identity{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000},
 	}
 	}
 	for _, testCase := range cases {
 	for _, testCase := range cases {
 		reader, err := TarWithOptions(filePath, testCase.opts)
 		reader, err := TarWithOptions(filePath, testCase.opts)

+ 3 - 3
pkg/archive/archive_unix.go

@@ -68,13 +68,13 @@ func getInodeFromStat(stat interface{}) (inode uint64, err error) {
 	return
 	return
 }
 }
 
 
-func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
+func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
 	s, ok := stat.(*syscall.Stat_t)
 	s, ok := stat.(*syscall.Stat_t)
 
 
 	if !ok {
 	if !ok {
-		return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t")
+		return idtools.Identity{}, errors.New("cannot convert stat value to syscall.Stat_t")
 	}
 	}
-	return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil
+	return idtools.Identity{UID: int(s.Uid), GID: int(s.Gid)}, nil
 }
 }
 
 
 // handleTarTypeBlockCharFifo is an OS-specific helper function used by
 // handleTarTypeBlockCharFifo is an OS-specific helper function used by

+ 2 - 2
pkg/archive/archive_windows.go

@@ -61,7 +61,7 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
 	return nil
 	return nil
 }
 }
 
 
-func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
+func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
 	// no notion of file ownership mapping yet on Windows
 	// no notion of file ownership mapping yet on Windows
-	return idtools.IDPair{UID: 0, GID: 0}, nil
+	return idtools.Identity{UID: 0, GID: 0}, nil
 }
 }

+ 2 - 2
pkg/archive/diff.go

@@ -33,7 +33,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
 	if options.ExcludePatterns == nil {
 	if options.ExcludePatterns == nil {
 		options.ExcludePatterns = []string{}
 		options.ExcludePatterns = []string{}
 	}
 	}
-	idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
+	idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
 
 
 	aufsTempdir := ""
 	aufsTempdir := ""
 	aufsHardlinks := make(map[string]*tar.Header)
 	aufsHardlinks := make(map[string]*tar.Header)
@@ -192,7 +192,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
 				srcData = tmpFile
 				srcData = tmpFile
 			}
 			}
 
 
-			if err := remapIDs(idMappings, srcHdr); err != nil {
+			if err := remapIDs(idMapping, srcHdr); err != nil {
 				return 0, err
 				return 0, err
 			}
 			}
 
 

+ 7 - 7
pkg/chrootarchive/archive.go

@@ -12,13 +12,13 @@ import (
 )
 )
 
 
 // NewArchiver returns a new Archiver which uses chrootarchive.Untar
 // NewArchiver returns a new Archiver which uses chrootarchive.Untar
-func NewArchiver(idMappings *idtools.IDMappings) *archive.Archiver {
-	if idMappings == nil {
-		idMappings = &idtools.IDMappings{}
+func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver {
+	if idMapping == nil {
+		idMapping = &idtools.IdentityMapping{}
 	}
 	}
 	return &archive.Archiver{
 	return &archive.Archiver{
-		Untar:         Untar,
-		IDMappingsVar: idMappings,
+		Untar:     Untar,
+		IDMapping: idMapping,
 	}
 	}
 }
 }
 
 
@@ -49,8 +49,8 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
 		options.ExcludePatterns = []string{}
 		options.ExcludePatterns = []string{}
 	}
 	}
 
 
-	idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
-	rootIDs := idMappings.RootPair()
+	idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
+	rootIDs := idMapping.RootPair()
 
 
 	dest = filepath.Clean(dest)
 	dest = filepath.Clean(dest)
 	if _, err := os.Stat(dest); os.IsNotExist(err) {
 	if _, err := os.Stat(dest); os.IsNotExist(err) {

+ 19 - 17
pkg/containerfs/archiver.go

@@ -22,11 +22,11 @@ type UntarFunc func(io.Reader, string, *archive.TarOptions) error
 
 
 // Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
 // Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
 type Archiver struct {
 type Archiver struct {
-	SrcDriver     Driver
-	DstDriver     Driver
-	Tar           TarFunc
-	Untar         UntarFunc
-	IDMappingsVar *idtools.IDMappings
+	SrcDriver Driver
+	DstDriver Driver
+	Tar       TarFunc
+	Untar     UntarFunc
+	IDMapping *idtools.IdentityMapping
 }
 }
 
 
 // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
 // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
@@ -39,8 +39,8 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
 	}
 	}
 	defer tarArchive.Close()
 	defer tarArchive.Close()
 	options := &archive.TarOptions{
 	options := &archive.TarOptions{
-		UIDMaps: archiver.IDMappingsVar.UIDs(),
-		GIDMaps: archiver.IDMappingsVar.GIDs(),
+		UIDMaps: archiver.IDMapping.UIDs(),
+		GIDMaps: archiver.IDMapping.GIDs(),
 	}
 	}
 	return archiver.Untar(tarArchive, dst, options)
 	return archiver.Untar(tarArchive, dst, options)
 }
 }
@@ -53,8 +53,8 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
 	}
 	}
 	defer tarArchive.Close()
 	defer tarArchive.Close()
 	options := &archive.TarOptions{
 	options := &archive.TarOptions{
-		UIDMaps: archiver.IDMappingsVar.UIDs(),
-		GIDMaps: archiver.IDMappingsVar.GIDs(),
+		UIDMaps: archiver.IDMapping.UIDs(),
+		GIDMaps: archiver.IDMapping.GIDs(),
 	}
 	}
 	return archiver.Untar(tarArchive, dst, options)
 	return archiver.Untar(tarArchive, dst, options)
 }
 }
@@ -75,9 +75,11 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
 	// if this archiver is set up with ID mapping we need to create
 	// if this archiver is set up with ID mapping we need to create
 	// the new destination directory with the remapped root UID/GID pair
 	// the new destination directory with the remapped root UID/GID pair
 	// as owner
 	// as owner
-	rootIDs := archiver.IDMappingsVar.RootPair()
+
+	identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}
+
 	// Create dst, copy src's content into it
 	// Create dst, copy src's content into it
-	if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
+	if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
 		return err
 		return err
 	}
 	}
 	logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
 	logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
@@ -150,7 +152,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 				hdr.Mode = int64(os.FileMode(hdr.Mode))
 				hdr.Mode = int64(os.FileMode(hdr.Mode))
 			}
 			}
 
 
-			if err := remapIDs(archiver.IDMappingsVar, hdr); err != nil {
+			if err := remapIDs(archiver.IDMapping, hdr); err != nil {
 				return err
 				return err
 			}
 			}
 
 
@@ -178,13 +180,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 	return err
 	return err
 }
 }
 
 
-// IDMappings returns the IDMappings of the archiver.
-func (archiver *Archiver) IDMappings() *idtools.IDMappings {
-	return archiver.IDMappingsVar
+// IdentityMapping returns the IdentityMapping of the archiver.
+func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
+	return archiver.IDMapping
 }
 }
 
 
-func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
-	ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
+func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
+	ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
 	hdr.Uid, hdr.Gid = ids.UID, ids.GID
 	hdr.Uid, hdr.Gid = ids.UID, ids.GID
 	return err
 	return err
 }
 }

+ 23 - 22
pkg/idtools/idtools.go

@@ -37,23 +37,23 @@ const (
 // MkdirAllAndChown creates a directory (include any along the path) and then modifies
 // MkdirAllAndChown creates a directory (include any along the path) and then modifies
 // ownership to the requested uid/gid.  If the directory already exists, this
 // ownership to the requested uid/gid.  If the directory already exists, this
 // function will still change ownership to the requested uid/gid pair.
 // function will still change ownership to the requested uid/gid pair.
-func MkdirAllAndChown(path string, mode os.FileMode, owner IDPair) error {
-	return mkdirAs(path, mode, owner.UID, owner.GID, true, true)
+func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error {
+	return mkdirAs(path, mode, owner, true, true)
 }
 }
 
 
 // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
 // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
 // If the directory already exists, this function still changes ownership.
 // If the directory already exists, this function still changes ownership.
 // Note that unlike os.Mkdir(), this function does not return IsExist error
 // Note that unlike os.Mkdir(), this function does not return IsExist error
 // in case path already exists.
 // in case path already exists.
-func MkdirAndChown(path string, mode os.FileMode, owner IDPair) error {
-	return mkdirAs(path, mode, owner.UID, owner.GID, false, true)
+func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
+	return mkdirAs(path, mode, owner, false, true)
 }
 }
 
 
 // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
 // 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
 // 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
 // directories along the path exist, no change of ownership will be performed
-func MkdirAllAndChownNew(path string, mode os.FileMode, owner IDPair) error {
-	return mkdirAs(path, mode, owner.UID, owner.GID, true, false)
+func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
+	return mkdirAs(path, mode, owner, true, false)
 }
 }
 
 
 // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
 // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
@@ -102,22 +102,23 @@ func toHost(contID int, idMap []IDMap) (int, error) {
 	return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
 	return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
 }
 }
 
 
-// IDPair is a UID and GID pair
-type IDPair struct {
+// Identity is either a UID and GID pair or a SID (but not both)
+type Identity struct {
 	UID int
 	UID int
 	GID int
 	GID int
+	SID string
 }
 }
 
 
-// IDMappings contains a mappings of UIDs and GIDs
-type IDMappings struct {
+// IdentityMapping contains a mappings of UIDs and GIDs
+type IdentityMapping struct {
 	uids []IDMap
 	uids []IDMap
 	gids []IDMap
 	gids []IDMap
 }
 }
 
 
-// NewIDMappings takes a requested user and group name and
+// NewIdentityMapping takes a requested user and group name and
 // using the data from /etc/sub{uid,gid} ranges, creates the
 // using the data from /etc/sub{uid,gid} ranges, creates the
 // proper uid and gid remapping ranges for that user/group pair
 // proper uid and gid remapping ranges for that user/group pair
-func NewIDMappings(username, groupname string) (*IDMappings, error) {
+func NewIdentityMapping(username, groupname string) (*IdentityMapping, error) {
 	subuidRanges, err := parseSubuid(username)
 	subuidRanges, err := parseSubuid(username)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -133,7 +134,7 @@ func NewIDMappings(username, groupname string) (*IDMappings, error) {
 		return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
 		return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
 	}
 	}
 
 
-	return &IDMappings{
+	return &IdentityMapping{
 		uids: createIDMap(subuidRanges),
 		uids: createIDMap(subuidRanges),
 		gids: createIDMap(subgidRanges),
 		gids: createIDMap(subgidRanges),
 	}, nil
 	}, nil
@@ -141,21 +142,21 @@ func NewIDMappings(username, groupname string) (*IDMappings, error) {
 
 
 // NewIDMappingsFromMaps creates a new mapping from two slices
 // NewIDMappingsFromMaps creates a new mapping from two slices
 // Deprecated: this is a temporary shim while transitioning to IDMapping
 // Deprecated: this is a temporary shim while transitioning to IDMapping
-func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings {
-	return &IDMappings{uids: uids, gids: gids}
+func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping {
+	return &IdentityMapping{uids: uids, gids: gids}
 }
 }
 
 
 // RootPair returns a uid and gid pair for the root user. The error is ignored
 // RootPair returns a uid and gid pair for the root user. The error is ignored
 // because a root user always exists, and the defaults are correct when the uid
 // because a root user always exists, and the defaults are correct when the uid
 // and gid maps are empty.
 // and gid maps are empty.
-func (i *IDMappings) RootPair() IDPair {
+func (i *IdentityMapping) RootPair() Identity {
 	uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
 	uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
-	return IDPair{UID: uid, GID: gid}
+	return Identity{UID: uid, GID: gid}
 }
 }
 
 
 // ToHost returns the host UID and GID for the container uid, gid.
 // ToHost returns the host UID and GID for the container uid, gid.
 // Remapping is only performed if the ids aren't already the remapped root ids
 // Remapping is only performed if the ids aren't already the remapped root ids
-func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
+func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) {
 	var err error
 	var err error
 	target := i.RootPair()
 	target := i.RootPair()
 
 
@@ -173,7 +174,7 @@ func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
 }
 }
 
 
 // ToContainer returns the container UID and GID for the host uid and gid
 // ToContainer returns the container UID and GID for the host uid and gid
-func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
+func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) {
 	uid, err := toContainer(pair.UID, i.uids)
 	uid, err := toContainer(pair.UID, i.uids)
 	if err != nil {
 	if err != nil {
 		return -1, -1, err
 		return -1, -1, err
@@ -183,19 +184,19 @@ func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
 }
 }
 
 
 // Empty returns true if there are no id mappings
 // Empty returns true if there are no id mappings
-func (i *IDMappings) Empty() bool {
+func (i *IdentityMapping) Empty() bool {
 	return len(i.uids) == 0 && len(i.gids) == 0
 	return len(i.uids) == 0 && len(i.gids) == 0
 }
 }
 
 
 // UIDs return the UID mapping
 // UIDs return the UID mapping
 // TODO: remove this once everything has been refactored to use pairs
 // TODO: remove this once everything has been refactored to use pairs
-func (i *IDMappings) UIDs() []IDMap {
+func (i *IdentityMapping) UIDs() []IDMap {
 	return i.uids
 	return i.uids
 }
 }
 
 
 // GIDs return the UID mapping
 // GIDs return the UID mapping
 // TODO: remove this once everything has been refactored to use pairs
 // TODO: remove this once everything has been refactored to use pairs
-func (i *IDMappings) GIDs() []IDMap {
+func (i *IdentityMapping) GIDs() []IDMap {
 	return i.gids
 	return i.gids
 }
 }
 
 

+ 5 - 4
pkg/idtools/idtools_unix.go

@@ -21,11 +21,12 @@ var (
 	getentCmd string
 	getentCmd string
 )
 )
 
 
-func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
+func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
 	// make an array containing the original path asked for, plus (for mkAll == true)
 	// make an array containing the original path asked for, plus (for mkAll == true)
 	// all path components leading up to the complete path that don't exist before we MkdirAll
 	// all path components leading up to the complete path that don't exist before we MkdirAll
 	// so that we can chown all of them properly at the end.  If chownExisting is false, we won't
 	// so that we can chown all of them properly at the end.  If chownExisting is false, we won't
 	// chown the full directory path if it exists
 	// chown the full directory path if it exists
+
 	var paths []string
 	var paths []string
 
 
 	stat, err := system.Stat(path)
 	stat, err := system.Stat(path)
@@ -38,7 +39,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
 		}
 		}
 
 
 		// short-circuit--we were called with an existing directory and chown was requested
 		// short-circuit--we were called with an existing directory and chown was requested
-		return lazyChown(path, ownerUID, ownerGID, stat)
+		return lazyChown(path, owner.UID, owner.GID, stat)
 	}
 	}
 
 
 	if os.IsNotExist(err) {
 	if os.IsNotExist(err) {
@@ -69,7 +70,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
 	// even if it existed, we will chown the requested path + any subpaths that
 	// even if it existed, we will chown the requested path + any subpaths that
 	// didn't exist when we called MkdirAll
 	// didn't exist when we called MkdirAll
 	for _, pathComponent := range paths {
 	for _, pathComponent := range paths {
-		if err := lazyChown(pathComponent, ownerUID, ownerGID, nil); err != nil {
+		if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -78,7 +79,7 @@ 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
 // 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
 // if that uid, gid pair has access (execute bit) to the directory
-func CanAccess(path string, pair IDPair) bool {
+func CanAccess(path string, pair Identity) bool {
 	statInfo, err := system.Stat(path)
 	statInfo, err := system.Stat(path)
 	if err != nil {
 	if err != nil {
 		return false
 		return false

+ 14 - 14
pkg/idtools/idtools_unix_test.go

@@ -46,7 +46,7 @@ func TestMkdirAllAndChown(t *testing.T) {
 	}
 	}
 
 
 	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
 	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
-	if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
+	if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, Identity{UID: 99, GID: 99}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	testTree["usr/share"] = node{99, 99}
 	testTree["usr/share"] = node{99, 99}
@@ -59,7 +59,7 @@ func TestMkdirAllAndChown(t *testing.T) {
 	}
 	}
 
 
 	// test 2-deep new directories--both should be owned by the uid/gid pair
 	// test 2-deep new directories--both should be owned by the uid/gid pair
-	if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}); err != nil {
+	if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, Identity{UID: 101, GID: 101}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	testTree["lib/some"] = node{101, 101}
 	testTree["lib/some"] = node{101, 101}
@@ -73,7 +73,7 @@ func TestMkdirAllAndChown(t *testing.T) {
 	}
 	}
 
 
 	// test a directory that already exists; should be chowned, but nothing else
 	// test a directory that already exists; should be chowned, but nothing else
-	if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
+	if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, Identity{UID: 102, GID: 102}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	testTree["usr"] = node{102, 102}
 	testTree["usr"] = node{102, 102}
@@ -102,7 +102,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
 	assert.NilError(t, buildTree(dirName, testTree))
 	assert.NilError(t, buildTree(dirName, testTree))
 
 
 	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
 	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
-	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99})
+	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, Identity{UID: 99, GID: 99})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	testTree["usr/share"] = node{99, 99}
 	testTree["usr/share"] = node{99, 99}
@@ -111,7 +111,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
 	assert.NilError(t, compareTrees(testTree, verifyTree))
 	assert.NilError(t, compareTrees(testTree, verifyTree))
 
 
 	// test 2-deep new directories--both should be owned by the uid/gid pair
 	// test 2-deep new directories--both should be owned by the uid/gid pair
-	err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101})
+	err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, Identity{UID: 101, GID: 101})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	testTree["lib/some"] = node{101, 101}
 	testTree["lib/some"] = node{101, 101}
 	testTree["lib/some/other"] = node{101, 101}
 	testTree["lib/some/other"] = node{101, 101}
@@ -120,7 +120,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
 	assert.NilError(t, compareTrees(testTree, verifyTree))
 	assert.NilError(t, compareTrees(testTree, verifyTree))
 
 
 	// test a directory that already exists; should NOT be chowned
 	// test a directory that already exists; should NOT be chowned
-	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102})
+	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, Identity{UID: 102, GID: 102})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	verifyTree, err = readTree(dirName, "")
 	verifyTree, err = readTree(dirName, "")
 	assert.NilError(t, err)
 	assert.NilError(t, err)
@@ -143,7 +143,7 @@ func TestMkdirAndChown(t *testing.T) {
 	}
 	}
 
 
 	// test a directory that already exists; should just chown to the requested uid/gid
 	// test a directory that already exists; should just chown to the requested uid/gid
-	if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
+	if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, Identity{UID: 99, GID: 99}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	testTree["usr"] = node{99, 99}
 	testTree["usr"] = node{99, 99}
@@ -156,12 +156,12 @@ func TestMkdirAndChown(t *testing.T) {
 	}
 	}
 
 
 	// create a subdir under a dir which doesn't exist--should fail
 	// create a subdir under a dir which doesn't exist--should fail
-	if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, IDPair{UID: 102, GID: 102}); err == nil {
+	if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, Identity{UID: 102, GID: 102}); err == nil {
 		t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
 		t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
 	}
 	}
 
 
 	// create a subdir under an existing dir; should only change the ownership of the new subdir
 	// create a subdir under an existing dir; should only change the ownership of the new subdir
-	if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
+	if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, Identity{UID: 102, GID: 102}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	testTree["usr/bin"] = node{102, 102}
 	testTree["usr/bin"] = node{102, 102}
@@ -326,19 +326,19 @@ func TestNewIDMappings(t *testing.T) {
 	group, err := user.LookupGroupId(string(gids[0]))
 	group, err := user.LookupGroupId(string(gids[0]))
 	assert.Check(t, err)
 	assert.Check(t, err)
 
 
-	idMappings, err := NewIDMappings(tempUser.Username, group.Name)
+	idMapping, err := NewIdentityMapping(tempUser.Username, group.Name)
 	assert.Check(t, err)
 	assert.Check(t, err)
 
 
-	rootUID, rootGID, err := GetRootUIDGID(idMappings.UIDs(), idMappings.GIDs())
+	rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDs(), idMapping.GIDs())
 	assert.Check(t, err)
 	assert.Check(t, err)
 
 
 	dirName, err := ioutil.TempDir("", "mkdirall")
 	dirName, err := ioutil.TempDir("", "mkdirall")
 	assert.Check(t, err, "Couldn't create temp directory")
 	assert.Check(t, err, "Couldn't create temp directory")
 	defer os.RemoveAll(dirName)
 	defer os.RemoveAll(dirName)
 
 
-	err = MkdirAllAndChown(dirName, 0700, IDPair{UID: rootUID, GID: rootGID})
+	err = MkdirAllAndChown(dirName, 0700, Identity{UID: rootUID, GID: rootGID})
 	assert.Check(t, err, "Couldn't change ownership of file path. Got error")
 	assert.Check(t, err, "Couldn't change ownership of file path. Got error")
-	assert.Check(t, CanAccess(dirName, idMappings.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
+	assert.Check(t, CanAccess(dirName, idMapping.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
 }
 }
 
 
 func TestLookupUserAndGroup(t *testing.T) {
 func TestLookupUserAndGroup(t *testing.T) {
@@ -388,7 +388,7 @@ func TestMkdirIsNotDir(t *testing.T) {
 	}
 	}
 	defer os.Remove(file.Name())
 	defer os.Remove(file.Name())
 
 
-	err = mkdirAs(file.Name(), 0755, 0, 0, false, false)
+	err = mkdirAs(file.Name(), 0755, Identity{UID: 0, GID: 0}, false, false)
 	assert.Check(t, is.Error(err, "mkdir "+file.Name()+": not a directory"))
 	assert.Check(t, is.Error(err, "mkdir "+file.Name()+": not a directory"))
 }
 }
 
 

+ 6 - 4
pkg/idtools/idtools_windows.go

@@ -6,9 +6,11 @@ import (
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 )
 )
 
 
-// Platforms such as Windows do not support the UID/GID concept. So make this
-// just a wrapper around system.MkdirAll.
-func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
+// This is currently a wrapper around MkdirAll, however, since currently
+// permissions aren't set through this path, the identity isn't utilized.
+// Ownership is handled elsewhere, but in the future could be support here
+// too.
+func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
 	if err := system.MkdirAll(path, mode, ""); err != nil {
 	if err := system.MkdirAll(path, mode, ""); err != nil {
 		return err
 		return err
 	}
 	}
@@ -18,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
 // 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
 // if that uid, gid pair has access (execute bit) to the directory
 // Windows does not require/support this function, so always return true
 // Windows does not require/support this function, so always return true
-func CanAccess(path string, pair IDPair) bool {
+func CanAccess(path string, identity Identity) bool {
 	return true
 	return true
 }
 }

+ 69 - 3
pkg/system/syscall_windows.go

@@ -2,16 +2,62 @@ package system // import "github.com/docker/docker/pkg/system"
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"syscall"
 	"unsafe"
 	"unsafe"
 
 
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/windows"
 	"golang.org/x/sys/windows"
 )
 )
 
 
+const (
+	OWNER_SECURITY_INFORMATION               = 0x00000001
+	GROUP_SECURITY_INFORMATION               = 0x00000002
+	DACL_SECURITY_INFORMATION                = 0x00000004
+	SACL_SECURITY_INFORMATION                = 0x00000008
+	LABEL_SECURITY_INFORMATION               = 0x00000010
+	ATTRIBUTE_SECURITY_INFORMATION           = 0x00000020
+	SCOPE_SECURITY_INFORMATION               = 0x00000040
+	PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
+	ACCESS_FILTER_SECURITY_INFORMATION       = 0x00000100
+	BACKUP_SECURITY_INFORMATION              = 0x00010000
+	PROTECTED_DACL_SECURITY_INFORMATION      = 0x80000000
+	PROTECTED_SACL_SECURITY_INFORMATION      = 0x40000000
+	UNPROTECTED_DACL_SECURITY_INFORMATION    = 0x20000000
+	UNPROTECTED_SACL_SECURITY_INFORMATION    = 0x10000000
+)
+
+const (
+	SE_UNKNOWN_OBJECT_TYPE = iota
+	SE_FILE_OBJECT
+	SE_SERVICE
+	SE_PRINTER
+	SE_REGISTRY_KEY
+	SE_LMSHARE
+	SE_KERNEL_OBJECT
+	SE_WINDOW_OBJECT
+	SE_DS_OBJECT
+	SE_DS_OBJECT_ALL
+	SE_PROVIDER_DEFINED_OBJECT
+	SE_WMIGUID_OBJECT
+	SE_REGISTRY_WOW64_32KEY
+)
+
+const (
+	SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
+)
+
+const (
+	ContainerAdministratorSidString = "S-1-5-93-2-1"
+	ContainerUserSidString          = "S-1-5-93-2-2"
+)
+
 var (
 var (
-	ntuserApiset       = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
-	procGetVersionExW  = modkernel32.NewProc("GetVersionExW")
-	procGetProductInfo = modkernel32.NewProc("GetProductInfo")
+	ntuserApiset                  = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
+	modadvapi32                   = windows.NewLazySystemDLL("advapi32.dll")
+	procGetVersionExW             = modkernel32.NewProc("GetVersionExW")
+	procGetProductInfo            = modkernel32.NewProc("GetProductInfo")
+	procSetNamedSecurityInfo      = modadvapi32.NewProc("SetNamedSecurityInfoW")
+	procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl")
 )
 )
 
 
 // OSVersion is a wrapper for Windows version information
 // OSVersion is a wrapper for Windows version information
@@ -125,3 +171,23 @@ func HasWin32KSupport() bool {
 	// APIs.
 	// APIs.
 	return ntuserApiset.Load() == nil
 	return ntuserApiset.Load() == nil
 }
 }
+
+func SetNamedSecurityInfo(objectName *uint16, objectType uint32, securityInformation uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *byte, sacl *byte) (result error) {
+	r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfo.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0)
+	if r0 != 0 {
+		result = syscall.Errno(r0)
+	}
+	return
+}
+
+func GetSecurityDescriptorDacl(securityDescriptor *byte, daclPresent *uint32, dacl **byte, daclDefaulted *uint32) (result error) {
+	r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(securityDescriptor)), uintptr(unsafe.Pointer(daclPresent)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclDefaulted)), 0, 0)
+	if r1 == 0 {
+		if e1 != 0 {
+			result = syscall.Errno(e1)
+		} else {
+			result = syscall.EINVAL
+		}
+	}
+	return
+}

+ 1 - 1
plugin/manager_linux.go

@@ -53,7 +53,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
 	}
 	}
 
 
 	rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
 	rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
-	if err := initlayer.Setup(rootFS, idtools.IDPair{UID: 0, GID: 0}); err != nil {
+	if err := initlayer.Setup(rootFS, idtools.Identity{UID: 0, GID: 0}); err != nil {
 		return errors.WithStack(err)
 		return errors.WithStack(err)
 	}
 	}
 
 

+ 12 - 12
volume/local/local.go

@@ -46,18 +46,18 @@ type activeMount struct {
 // New instantiates a new Root instance with the provided scope. Scope
 // New instantiates a new Root instance with the provided scope. Scope
 // is the base path that the Root instance uses to store its
 // is the base path that the Root instance uses to store its
 // volumes. The base path is created here if it does not exist.
 // volumes. The base path is created here if it does not exist.
-func New(scope string, rootIDs idtools.IDPair) (*Root, error) {
+func New(scope string, rootIdentity idtools.Identity) (*Root, error) {
 	rootDirectory := filepath.Join(scope, volumesPathName)
 	rootDirectory := filepath.Join(scope, volumesPathName)
 
 
-	if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIDs); err != nil {
+	if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIdentity); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	r := &Root{
 	r := &Root{
-		scope:   scope,
-		path:    rootDirectory,
-		volumes: make(map[string]*localVolume),
-		rootIDs: rootIDs,
+		scope:        scope,
+		path:         rootDirectory,
+		volumes:      make(map[string]*localVolume),
+		rootIdentity: rootIdentity,
 	}
 	}
 
 
 	dirs, err := ioutil.ReadDir(rootDirectory)
 	dirs, err := ioutil.ReadDir(rootDirectory)
@@ -101,11 +101,11 @@ func New(scope string, rootIDs idtools.IDPair) (*Root, error) {
 // manages the creation/removal of volumes. It uses only standard vfs
 // manages the creation/removal of volumes. It uses only standard vfs
 // commands to create/remove dirs within its provided scope.
 // commands to create/remove dirs within its provided scope.
 type Root struct {
 type Root struct {
-	m       sync.Mutex
-	scope   string
-	path    string
-	volumes map[string]*localVolume
-	rootIDs idtools.IDPair
+	m            sync.Mutex
+	scope        string
+	path         string
+	volumes      map[string]*localVolume
+	rootIdentity idtools.Identity
 }
 }
 
 
 // List lists all the volumes
 // List lists all the volumes
@@ -146,7 +146,7 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error
 	}
 	}
 
 
 	path := r.DataPath(name)
 	path := r.DataPath(name)
-	if err := idtools.MkdirAllAndChown(path, 0755, r.rootIDs); err != nil {
+	if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil {
 		return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
 		return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
 	}
 	}
 
 

+ 9 - 9
volume/local/local_test.go

@@ -38,7 +38,7 @@ func TestRemove(t *testing.T) {
 	}
 	}
 	defer os.RemoveAll(rootDir)
 	defer os.RemoveAll(rootDir)
 
 
-	r, err := New(rootDir, idtools.IDPair{UID: os.Geteuid(), GID: os.Getegid()})
+	r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -80,7 +80,7 @@ func TestInitializeWithVolumes(t *testing.T) {
 	}
 	}
 	defer os.RemoveAll(rootDir)
 	defer os.RemoveAll(rootDir)
 
 
-	r, err := New(rootDir, idtools.IDPair{UID: os.Geteuid(), GID: os.Getegid()})
+	r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -90,7 +90,7 @@ func TestInitializeWithVolumes(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -112,7 +112,7 @@ func TestCreate(t *testing.T) {
 	}
 	}
 	defer os.RemoveAll(rootDir)
 	defer os.RemoveAll(rootDir)
 
 
-	r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -149,7 +149,7 @@ func TestCreate(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
-	r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -186,7 +186,7 @@ func TestCreateWithOpts(t *testing.T) {
 	}
 	}
 	defer os.RemoveAll(rootDir)
 	defer os.RemoveAll(rootDir)
 
 
-	r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -261,7 +261,7 @@ func TestCreateWithOpts(t *testing.T) {
 		t.Fatal("expected mount to still be active")
 		t.Fatal("expected mount to still be active")
 	}
 	}
 
 
-	r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
+	r, err = New(rootDir, idtools.Identity{UID: 0, GID: 0})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -283,7 +283,7 @@ func TestRelaodNoOpts(t *testing.T) {
 	}
 	}
 	defer os.RemoveAll(rootDir)
 	defer os.RemoveAll(rootDir)
 
 
-	r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -311,7 +311,7 @@ func TestRelaodNoOpts(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 1 - 1
volume/mounts/mounts.go

@@ -95,7 +95,7 @@ func (m *MountPoint) Cleanup() error {
 // configured, or creating the source directory if supplied.
 // configured, or creating the source directory if supplied.
 // The, optional, checkFun parameter allows doing additional checking
 // The, optional, checkFun parameter allows doing additional checking
 // before creating the source directory on the host.
 // before creating the source directory on the host.
-func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) {
+func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, err error) {
 	if m.SkipMountpointCreation {
 	if m.SkipMountpointCreation {
 		return m.Source, nil
 		return m.Source, nil
 	}
 	}

+ 1 - 1
volume/service/default_driver.go

@@ -9,7 +9,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
-func setupDefaultDriver(store *drivers.Store, root string, rootIDs idtools.IDPair) error {
+func setupDefaultDriver(store *drivers.Store, root string, rootIDs idtools.Identity) error {
 	d, err := local.New(root, rootIDs)
 	d, err := local.New(root, rootIDs)
 	if err != nil {
 	if err != nil {
 		return errors.Wrap(err, "error setting up default driver")
 		return errors.Wrap(err, "error setting up default driver")

+ 1 - 1
volume/service/default_driver_stubs.go

@@ -7,4 +7,4 @@ import (
 	"github.com/docker/docker/volume/drivers"
 	"github.com/docker/docker/volume/drivers"
 )
 )
 
 
-func setupDefaultDriver(_ *drivers.Store, _ string, _ idtools.IDPair) error { return nil }
+func setupDefaultDriver(_ *drivers.Store, _ string, _ idtools.Identity) error { return nil }

+ 1 - 1
volume/service/service.go

@@ -35,7 +35,7 @@ type VolumesService struct {
 }
 }
 
 
 // NewVolumeService creates a new volume service
 // NewVolumeService creates a new volume service
-func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.IDPair, logger volumeEventLogger) (*VolumesService, error) {
+func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.Identity, logger volumeEventLogger) (*VolumesService, error) {
 	ds := drivers.NewStore(pg)
 	ds := drivers.NewStore(pg)
 	if err := setupDefaultDriver(ds, root, rootIDs); err != nil {
 	if err := setupDefaultDriver(ds, root, rootIDs); err != nil {
 		return nil, err
 		return nil, err

+ 1 - 1
volume/service/service_linux_test.go

@@ -25,7 +25,7 @@ func TestLocalVolumeSize(t *testing.T) {
 	assert.Assert(t, err)
 	assert.Assert(t, err)
 	defer os.RemoveAll(dir)
 	defer os.RemoveAll(dir)
 
 
-	l, err := local.New(dir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
+	l, err := local.New(dir, idtools.Identity{UID: os.Getuid(), GID: os.Getegid()})
 	assert.Assert(t, err)
 	assert.Assert(t, err)
 	assert.Assert(t, ds.Register(l, volume.DefaultDriverName))
 	assert.Assert(t, ds.Register(l, volume.DefaultDriverName))
 	assert.Assert(t, ds.Register(testutils.NewFakeDriver("fake"), "fake"))
 	assert.Assert(t, ds.Register(testutils.NewFakeDriver("fake"), "fake"))