diff --git a/builder/builder-next/adapters/snapshot/snapshot.go b/builder/builder-next/adapters/snapshot/snapshot.go index 68c029c7c8..656c79ec20 100644 --- a/builder/builder-next/adapters/snapshot/snapshot.go +++ b/builder/builder-next/adapters/snapshot/snapshot.go @@ -32,7 +32,7 @@ type Opt struct { GraphDriver graphdriver.Driver LayerStore layer.Store Root string - IdentityMapping *idtools.IdentityMapping + IdentityMapping idtools.IdentityMapping } type graphIDRegistrar interface { @@ -100,7 +100,12 @@ func (s *snapshotter) Name() string { } func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping { - return s.opt.IdentityMapping + // Returning a non-nil but empty *IdentityMapping breaks BuildKit: + // https://github.com/moby/moby/pull/39444 + if s.opt.IdentityMapping.Empty() { + return nil + } + return &s.opt.IdentityMapping } func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { @@ -482,7 +487,7 @@ type mountable struct { acquire func() ([]mount.Mount, func() error, error) release func() error refCount int - idmap *idtools.IdentityMapping + idmap idtools.IdentityMapping } func (m *mountable) Mount() ([]mount.Mount, func() error, error) { @@ -527,5 +532,10 @@ func (m *mountable) releaseMount() error { } func (m *mountable) IdentityMapping() *idtools.IdentityMapping { - return m.idmap + // Returning a non-nil but empty *IdentityMapping breaks BuildKit: + // https://github.com/moby/moby/pull/39444 + if m.idmap.Empty() { + return nil + } + return &m.idmap } diff --git a/builder/builder-next/builder.go b/builder/builder-next/builder.go index d28e89825e..81688e2185 100644 --- a/builder/builder-next/builder.go +++ b/builder/builder-next/builder.go @@ -73,7 +73,7 @@ type Opt struct { RegistryHosts docker.RegistryHosts BuilderConfig config.BuilderConfig Rootless bool - IdentityMapping *idtools.IdentityMapping + IdentityMapping idtools.IdentityMapping DNSConfig config.DNSConfig ApparmorProfile string } @@ -91,10 +91,6 @@ type Builder struct { func New(opt Opt) (*Builder, error) { reqHandler := newReqBodyHandler(tracing.DefaultTransport) - if opt.IdentityMapping != nil && opt.IdentityMapping.Empty() { - opt.IdentityMapping = nil - } - c, err := newController(reqHandler, opt) if err != nil { return nil, err diff --git a/builder/builder-next/executor_unix.go b/builder/builder-next/executor_unix.go index 468f130523..55209d8c8a 100644 --- a/builder/builder-next/executor_unix.go +++ b/builder/builder-next/executor_unix.go @@ -25,7 +25,7 @@ import ( const networkName = "bridge" -func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, dnsConfig *oci.DNSConfig, rootless bool, idmap *idtools.IdentityMapping, apparmorProfile string) (executor.Executor, error) { +func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, dnsConfig *oci.DNSConfig, rootless bool, idmap idtools.IdentityMapping, apparmorProfile string) (executor.Executor, error) { netRoot := filepath.Join(root, "net") networkProviders := map[pb.NetMode]network.Provider{ pb.NetMode_UNSET: &bridgeProvider{NetworkController: net, Root: netRoot}, @@ -44,13 +44,20 @@ func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, dn } } + // Returning a non-nil but empty *IdentityMapping breaks BuildKit: + // https://github.com/moby/moby/pull/39444 + pidmap := &idmap + if idmap.Empty() { + pidmap = nil + } + return runcexecutor.New(runcexecutor.Opt{ Root: filepath.Join(root, "executor"), CommandCandidates: []string{"runc"}, DefaultCgroupParent: cgroupParent, Rootless: rootless, NoPivot: os.Getenv("DOCKER_RAMDISK") != "", - IdentityMapping: idmap, + IdentityMapping: pidmap, DNS: dnsConfig, ApparmorProfile: apparmorProfile, }, networkProviders) diff --git a/builder/builder-next/executor_windows.go b/builder/builder-next/executor_windows.go index 77d1fa096d..a14fa2dc9a 100644 --- a/builder/builder-next/executor_windows.go +++ b/builder/builder-next/executor_windows.go @@ -11,7 +11,7 @@ import ( "github.com/moby/buildkit/executor/oci" ) -func newExecutor(_, _ string, _ libnetwork.NetworkController, _ *oci.DNSConfig, _ bool, _ *idtools.IdentityMapping, _ string) (executor.Executor, error) { +func newExecutor(_, _ string, _ libnetwork.NetworkController, _ *oci.DNSConfig, _ bool, _ idtools.IdentityMapping, _ string) (executor.Executor, error) { return &winExecutor{}, nil } diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index ecfe8eaa1d..913d6ab59c 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -46,13 +46,13 @@ const ( // BuildManager is shared across all Builder objects type BuildManager struct { - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping backend builder.Backend pathCache pathCache // TODO: make this persistent } // NewBuildManager creates a BuildManager -func NewBuildManager(b builder.Backend, identityMapping *idtools.IdentityMapping) (*BuildManager, error) { +func NewBuildManager(b builder.Backend, identityMapping idtools.IdentityMapping) (*BuildManager, error) { bm := &BuildManager{ backend: b, pathCache: &syncmap.Map{}, @@ -103,7 +103,7 @@ type builderOptions struct { Backend builder.Backend ProgressWriter backend.ProgressWriter PathCache pathCache - IDMapping *idtools.IdentityMapping + IDMapping idtools.IdentityMapping } // Builder is a Dockerfile builder @@ -119,7 +119,7 @@ type Builder struct { docker builder.Backend clientCtx context.Context - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping disableCommit bool imageSources *imageSources pathCache pathCache diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 0bc9d0e9ef..1bc445d383 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -33,7 +33,7 @@ type Archiver interface { UntarPath(src, dst string) error CopyWithTar(src, dst string) error CopyFileWithTar(src, dst string) error - IdentityMapping() *idtools.IdentityMapping + IdentityMapping() idtools.IdentityMapping } // The builder will use the following interfaces if the container fs implements diff --git a/builder/dockerfile/internals_linux.go b/builder/dockerfile/internals_linux.go index 269f722d61..d4c714241f 100644 --- a/builder/dockerfile/internals_linux.go +++ b/builder/dockerfile/internals_linux.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" ) -func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) { +func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) { var userStr, grpStr string parts := strings.Split(chown, ":") if len(parts) > 2 { diff --git a/builder/dockerfile/internals_linux_test.go b/builder/dockerfile/internals_linux_test.go index 6a2a8f6a89..75af92ab5f 100644 --- a/builder/dockerfile/internals_linux_test.go +++ b/builder/dockerfile/internals_linux_test.go @@ -34,8 +34,8 @@ othergrp:x:6666: Size: 65536, }, } - remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps) - unmapped := &idtools.IdentityMapping{} + remapped := idtools.IdentityMapping{UIDMaps: idMaps, GIDMaps: idMaps} + unmapped := idtools.IdentityMapping{} contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test") defer cleanup() @@ -53,7 +53,7 @@ othergrp:x:6666: builder *Builder name string chownStr string - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping state *dispatchState expected idtools.Identity }{ @@ -126,7 +126,7 @@ othergrp:x:6666: builder *Builder name string chownStr string - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping state *dispatchState descr string }{ diff --git a/builder/dockerfile/internals_windows.go b/builder/dockerfile/internals_windows.go index 0d26e69ef3..335f87cdc7 100644 --- a/builder/dockerfile/internals_windows.go +++ b/builder/dockerfile/internals_windows.go @@ -14,7 +14,7 @@ import ( "golang.org/x/sys/windows" ) -func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) { +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) } diff --git a/contrib/docker-device-tool/device_tool.go b/contrib/docker-device-tool/device_tool.go index a9f3d720c3..f412afbd0c 100644 --- a/contrib/docker-device-tool/device_tool.go +++ b/contrib/docker-device-tool/device_tool.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/daemon/graphdriver/devmapper" "github.com/docker/docker/pkg/devicemapper" + "github.com/docker/docker/pkg/idtools" "github.com/sirupsen/logrus" ) @@ -76,7 +77,7 @@ func main() { args := flag.Args() home := path.Join(*root, "devicemapper") - devices, err := devmapper.NewDeviceSet(home, false, nil, nil, nil) + devices, err := devmapper.NewDeviceSet(home, false, nil, idtools.IdentityMapping{}) if err != nil { fmt.Println("Can't initialize device mapper: ", err) os.Exit(1) diff --git a/daemon/archive_tarcopyoptions.go b/daemon/archive_tarcopyoptions.go index ab81dc4a10..2e2141042e 100644 --- a/daemon/archive_tarcopyoptions.go +++ b/daemon/archive_tarcopyoptions.go @@ -9,7 +9,6 @@ import ( func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions { return &archive.TarOptions{ NoOverwriteDirNonDir: noOverwriteDirNonDir, - UIDMaps: daemon.idMapping.UIDs(), - GIDMaps: daemon.idMapping.GIDs(), + IDMap: daemon.idMapping, } } diff --git a/daemon/daemon.go b/daemon/daemon.go index 0c22deffda..cdcb6e555b 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -98,7 +98,7 @@ type Daemon struct { sysInfoOnce sync.Once sysInfo *sysinfo.SysInfo shutdown bool - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping graphDriver string // TODO: move graphDriver field to an InfoService PluginStore *plugin.Store // TODO: remove pluginManager *plugin.Manager @@ -1459,7 +1459,7 @@ func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore { } // IdentityMapping returns uid/gid mapping or a SID (in the case of Windows) for the builder -func (daemon *Daemon) IdentityMapping() *idtools.IdentityMapping { +func (daemon *Daemon) IdentityMapping() idtools.IdentityMapping { return daemon.idMapping } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index a92c675c8f..7895c4adb5 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1062,7 +1062,7 @@ func removeDefaultBridgeInterface() { } } -func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error { +func setupInitLayer(idMapping idtools.IdentityMapping) func(containerfs.ContainerFS) error { return func(initPath containerfs.ContainerFS) error { return initlayer.Setup(initPath, idMapping.RootPair()) } @@ -1161,9 +1161,9 @@ func parseRemappedRoot(usergrp string) (string, string, error) { return username, groupname, nil } -func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) { +func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { if runtime.GOOS != "linux" && config.RemappedRoot != "" { - return nil, fmt.Errorf("User namespaces are only supported on Linux") + return idtools.IdentityMapping{}, fmt.Errorf("User namespaces are only supported on Linux") } // if the daemon was started with remapped root option, parse @@ -1171,25 +1171,25 @@ func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) if config.RemappedRoot != "" { username, groupname, err := parseRemappedRoot(config.RemappedRoot) if err != nil { - return nil, err + return idtools.IdentityMapping{}, err } if username == "root" { // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op // effectively logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF") - return &idtools.IdentityMapping{}, nil + return idtools.IdentityMapping{}, nil } logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s", username) // update remapped root setting now that we have resolved them to actual names config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) - mappings, err := idtools.NewIdentityMapping(username) + mappings, err := idtools.LoadIdentityMapping(username) if err != nil { - return nil, errors.Wrap(err, "Can't create ID mappings") + return idtools.IdentityMapping{}, errors.Wrap(err, "Can't create ID mappings") } return mappings, nil } - return &idtools.IdentityMapping{}, nil + return idtools.IdentityMapping{}, nil } func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) error { diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 13907d0ea7..6579296e17 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -63,7 +63,7 @@ func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfi return nil } -func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error { +func setupInitLayer(idMapping idtools.IdentityMapping) func(containerfs.ContainerFS) error { return nil } @@ -437,8 +437,8 @@ func recursiveUnmount(_ string) error { return nil } -func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) { - return &idtools.IdentityMapping{}, nil +func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { + return idtools.IdentityMapping{}, nil } func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error { diff --git a/daemon/export.go b/daemon/export.go index 49fe778664..b248def224 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -63,8 +63,7 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R archv, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{ Compression: archive.Uncompressed, - UIDMaps: daemon.idMapping.UIDs(), - GIDMaps: daemon.idMapping.GIDs(), + IDMap: daemon.idMapping, }, basefs.Path()) if err != nil { rwlayer.Unmount() diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 6a4afae180..a8e5ff9609 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -71,8 +71,7 @@ func init() { // Driver contains information about the filesystem mounted. type Driver struct { root string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping ctr *graphdriver.RefCounter pathCacheLock sync.Mutex pathCache map[string]string @@ -83,7 +82,7 @@ type Driver struct { // Init returns a new AUFS driver. // An error is returned if AUFS is not supported. -func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(root string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { // Try to load the aufs kernel module if err := supportsAufs(); err != nil { logger.Error(err) @@ -121,21 +120,16 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap a := &Driver{ root: root, - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, pathCache: make(map[string]string), ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)), locker: locker.New(), } currentID := idtools.CurrentIdentity() - _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } dirID := idtools.Identity{ UID: currentID.UID, - GID: rootGID, + GID: a.idMap.RootPair().GID, } // Create the root aufs driver dir @@ -170,7 +164,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } } - a.naiveDiff = graphdriver.NewNaiveDiffDriver(a, uidMaps, gidMaps) + a.naiveDiff = graphdriver.NewNaiveDiffDriver(a, a.idMap) return a, nil } @@ -285,15 +279,11 @@ func (a *Driver) createDirsFor(id string) error { "diff", } - rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) - if err != nil { - return err - } // Directory permission is 0755. // The path of directories are /mnt/ // and /diff/ for _, p := range paths { - if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, a.idMap.RootPair()); err != nil { return err } } @@ -439,8 +429,7 @@ func (a *Driver) Diff(id, parent string) (io.ReadCloser, error) { return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ Compression: archive.Uncompressed, ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, - UIDMaps: a.uidMaps, - GIDMaps: a.gidMaps, + IDMap: a.idMap, }) } @@ -461,8 +450,7 @@ func (a *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { func (a *Driver) applyDiff(id string, diff io.Reader) error { return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ - UIDMaps: a.uidMaps, - GIDMaps: a.gidMaps, + IDMap: a.idMap, }) } diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index 4c902f7848..377c050e29 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" @@ -31,7 +32,7 @@ func init() { } func testInit(dir string, t testing.TB) graphdriver.Driver { - d, err := Init(dir, nil, nil, nil) + d, err := Init(dir, nil, idtools.IdentityMapping{}) if err != nil { if err == graphdriver.ErrNotSupported { t.Skip(err) diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index bac84c9271..d51f04709e 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/daemon/graphdriver/btrfs/btrfs.go @@ -50,7 +50,7 @@ type btrfsOptions struct { // Init returns a new BTRFS driver. // An error is returned if BTRFS is not supported. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { // Perform feature detection on /var/lib/docker/btrfs if it's an existing directory. // This covers situations where /var/lib/docker/btrfs is a mount, and on a different @@ -70,11 +70,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, graphdriver.ErrPrerequisites } - remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps) currentID := idtools.CurrentIdentity() dirID := idtools.Identity{ UID: currentID.UID, - GID: remappedRoot.RootPair().GID, + GID: idMap.RootPair().GID, } if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { @@ -97,8 +96,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap driver := &Driver{ home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, options: opt, } @@ -108,7 +106,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } } - return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil + return graphdriver.NewNaiveDiffDriver(driver, driver.idMap), nil } func parseOptions(opt []string) (btrfsOptions, bool, error) { @@ -139,8 +137,7 @@ func parseOptions(opt []string) (btrfsOptions, bool, error) { type Driver struct { // root of the file system home string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping options btrfsOptions quotaEnabled bool once sync.Once @@ -490,15 +487,12 @@ func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { quotas := path.Join(d.home, "quotas") subvolumes := path.Join(d.home, "subvolumes") - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return err - } + root := d.idMap.RootPair() currentID := idtools.CurrentIdentity() dirID := idtools.Identity{ UID: currentID.UID, - GID: rootGID, + GID: root.GID, } if err := idtools.MkdirAllAndChown(subvolumes, 0710, dirID); err != nil { @@ -546,8 +540,8 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { // if we have a remapped root (user namespaces enabled), change the created snapshot // dir ownership to match - if rootUID != 0 || rootGID != 0 { - if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { + if root.UID != 0 || root.GID != 0 { + if err := root.Chown(path.Join(subvolumes, id)); err != nil { return err } } diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index e9c4634b67..739c9a010a 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -117,8 +117,7 @@ type DeviceSet struct { BaseDeviceFilesystem string // save filesystem of base device nrDeletedDevices uint // number of deleted devices deletionWorkerTicker *time.Ticker - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping minFreeSpacePercent uint32 // min free space percentage in thinpool xfsNospaceRetries string // max retries when xfs receives ENOSPC lvmSetupConfig directLVMConfig @@ -264,11 +263,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { dirname := devices.loopbackDir() filename := path.Join(dirname, name) - uid, gid, err := idtools.GetRootUIDGID(devices.uidMaps, devices.gidMaps) - if err != nil { - return "", err - } - if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil { + if err := idtools.MkdirAllAndChown(dirname, 0700, devices.idMap.RootPair()); err != nil { return "", err } @@ -1694,11 +1689,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // create the root dir of the devmapper driver ownership to match this // daemon's remapped root uid/gid so containers can start properly - uid, gid, err := idtools.GetRootUIDGID(devices.uidMaps, devices.gidMaps) - if err != nil { - return err - } - if err := idtools.MkdirAndChown(devices.root, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil { + if err := idtools.MkdirAndChown(devices.root, 0700, devices.idMap.RootPair()); err != nil { return err } if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil { @@ -2622,7 +2613,7 @@ func (devices *DeviceSet) exportDeviceMetadata(hash string) (*deviceMetadata, er } // NewDeviceSet creates the device set based on the options provided. -func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps []idtools.IDMap) (*DeviceSet, error) { +func NewDeviceSet(root string, doInit bool, options []string, idMap idtools.IdentityMapping) (*DeviceSet, error) { devicemapper.SetDevDir("/dev") devices := &DeviceSet{ @@ -2636,8 +2627,7 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ thinpBlockSize: defaultThinpBlockSize, deviceIDMap: make([]byte, deviceIDMapSz), deletionWorkerTicker: time.NewTicker(time.Second * 30), - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, minFreeSpacePercent: defaultMinFreeSpacePercent, } diff --git a/daemon/graphdriver/devmapper/devmapper_test.go b/daemon/graphdriver/devmapper/devmapper_test.go index 7eb1795d17..1567d520e7 100644 --- a/daemon/graphdriver/devmapper/devmapper_test.go +++ b/daemon/graphdriver/devmapper/devmapper_test.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver/graphtest" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers/kernel" "golang.org/x/sys/unix" ) @@ -115,7 +116,7 @@ func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataS d, err := Init(driver.home, []string{ fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta), fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta), - }, nil, nil) + }, idtools.IdentityMapping{}) if err != nil { t.Fatalf("error creating devicemapper driver: %v", err) } diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 7abdbe6163..6c2a80b6cb 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -27,16 +27,14 @@ func init() { // Driver contains the device set mounted and the home directory type Driver struct { *DeviceSet - home string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap - ctr *graphdriver.RefCounter - locker *locker.Locker + home string + ctr *graphdriver.RefCounter + locker *locker.Locker } // Init creates a driver with the given home and the set of options. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home, true, options, uidMaps, gidMaps) +func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { + deviceSet, err := NewDeviceSet(home, true, options, idMap) if err != nil { return nil, err } @@ -44,13 +42,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d := &Driver{ DeviceSet: deviceSet, home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), locker: locker.New(), } - return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil + return graphdriver.NewNaiveDiffDriver(d, d.idMap), nil } func (d *Driver) String() string { @@ -188,18 +184,14 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { return containerfs.NewLocalContainerFS(rootFs), nil } - uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - d.ctr.Decrement(mp) - return nil, err - } + root := d.idMap.RootPair() // Create the target directories if they don't exist - if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.Identity{UID: uid, GID: gid}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, root); err != nil { d.ctr.Decrement(mp) return nil, err } - if err := idtools.MkdirAndChown(mp, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAndChown(mp, 0755, root); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) return nil, err } @@ -210,7 +202,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { return nil, err } - if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil { + if err := idtools.MkdirAllAndChown(rootFs, 0755, root); err != nil { d.ctr.Decrement(mp) d.DeviceSet.UnmountDevice(id, mp) return nil, err diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index a9e8ce4c91..f73f66a677 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -37,7 +37,7 @@ type CreateOpts struct { } // InitFunc initializes the storage driver. -type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) +type InitFunc func(root string, options []string, idMap idtools.IdentityMapping) (Driver, error) // ProtoDriver defines the basic capabilities of a driver. // This interface exists solely to be a minimum set of methods @@ -162,7 +162,7 @@ func Register(name string, initFunc InitFunc) error { // GetDriver initializes and returns the registered driver func GetDriver(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) { if initFunc, exists := drivers[name]; exists { - return initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps) + return initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.IDMap) } pluginDriver, err := lookupPlugin(name, pg, config) @@ -174,9 +174,9 @@ func GetDriver(name string, pg plugingetter.PluginGetter, config Options) (Drive } // getBuiltinDriver initializes and returns the registered driver, but does not try to load from plugins -func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) { +func getBuiltinDriver(name, home string, options []string, idMap idtools.IdentityMapping) (Driver, error) { if initFunc, exists := drivers[name]; exists { - return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps) + return initFunc(filepath.Join(home, name), options, idMap) } logrus.Errorf("Failed to built-in GetDriver graph %s %s", name, home) return nil, ErrNotSupported @@ -186,8 +186,7 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id type Options struct { Root string DriverOptions []string - UIDMaps []idtools.IDMap - GIDMaps []idtools.IDMap + IDMap idtools.IdentityMapping ExperimentalEnabled bool } @@ -211,7 +210,7 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err if _, prior := driversMap[name]; prior { // of the state found from prior drivers, check in order of our priority // which we would prefer - driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.IDMap) if err != nil { // unlike below, we will return error here, because there is prior // state, and now it is no longer supported/prereq/compatible, so @@ -240,7 +239,7 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err // Check for priority drivers first for _, name := range list { - driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.IDMap) if err != nil { if IsDriverNotSupported(err) { continue @@ -258,7 +257,7 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err // can be selected through configuration. continue } - driver, err := initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.IDMap) if err != nil { if IsDriverNotSupported(err) { continue diff --git a/daemon/graphdriver/fsdiff.go b/daemon/graphdriver/fsdiff.go index f06caedb92..92f34e2997 100644 --- a/daemon/graphdriver/fsdiff.go +++ b/daemon/graphdriver/fsdiff.go @@ -24,8 +24,7 @@ var ( // Notably, the AUFS driver doesn't need to be wrapped like this. type NaiveDiffDriver struct { ProtoDriver - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping } // NewNaiveDiffDriver returns a fully functional driver that wraps the @@ -35,10 +34,9 @@ type NaiveDiffDriver struct { // Changes(id, parent string) ([]archive.Change, error) // ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) // DiffSize(id, parent string) (size int64, err error) -func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Driver { +func NewNaiveDiffDriver(driver ProtoDriver, idMap idtools.IdentityMapping) Driver { return &NaiveDiffDriver{ProtoDriver: driver, - uidMaps: uidMaps, - gidMaps: gidMaps} + idMap: idMap} } // Diff produces an archive of the changes between the specified @@ -84,7 +82,7 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err err return nil, err } - archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps) + archive, err := archive.ExportChanges(layerFs, changes, gdw.idMap) if err != nil { return nil, err } @@ -142,8 +140,7 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size i defer driver.Put(id) layerFs := layerRootFs.Path() - options := &archive.TarOptions{UIDMaps: gdw.uidMaps, - GIDMaps: gdw.gidMaps} + options := &archive.TarOptions{IDMap: gdw.idMap} start := time.Now().UTC() logrus.WithField("id", id).Debug("Start untar layer") if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil { diff --git a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go index 137b7b189a..baaa24b291 100644 --- a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go +++ b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go @@ -60,8 +60,7 @@ const ( // mounts that are created using this driver. type Driver struct { home string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping ctr *graphdriver.RefCounter naiveDiff graphdriver.DiffDriver locker *locker.Locker @@ -78,7 +77,7 @@ func init() { // Init returns the naive diff driver for fuse-overlayfs. // If fuse-overlayfs is not supported on the host, the error // graphdriver.ErrNotSupported is returned. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { if _, err := exec.LookPath(binary); err != nil { logger.Error(err) return nil, graphdriver.ErrNotSupported @@ -87,11 +86,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, graphdriver.ErrNotSupported } - remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps) currentID := idtools.CurrentIdentity() dirID := idtools.Identity{ UID: currentID.UID, - GID: remappedRoot.RootPair().GID, + GID: idMap.RootPair().GID, } if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { @@ -102,14 +100,13 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } d := &Driver{ - home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, - ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicFUSE)), - locker: locker.New(), + home: home, + idMap: idMap, + ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicFUSE)), + locker: locker.New(), } - d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) + d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, idMap) return d, nil } @@ -175,22 +172,12 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { dir := d.dir(id) + root := d.idMap.RootPair() - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, root); err != nil { return err } - root := idtools.Identity{UID: rootUID, GID: rootGID} - - dirID := idtools.Identity{ - UID: rootUID, - GID: rootGID, - } - - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil { - return err - } - if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { + if err := idtools.MkdirAndChown(dir, 0710, root); err != nil { return err } @@ -224,7 +211,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return nil } - if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0710, dirID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0710, root); err != nil { return err } @@ -377,11 +364,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e mountData := label.FormatMountLabel(opts, mountLabel) mountTarget := mergedDir - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return nil, err - } - if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAndChown(mergedDir, 0700, d.idMap.RootPair()); err != nil { return nil, err } @@ -477,8 +460,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 logger.Debugf("Applying tar in %s", applyDir) // Overlay doesn't need the parent id to apply the diff if err := untar(diff, applyDir, &archive.TarOptions{ - UIDMaps: d.uidMaps, - GIDMaps: d.gidMaps, + IDMap: d.idMap, // Use AUFS whiteout format: https://github.com/containers/storage/blob/39a8d5ed9843844eafb5d2ba6e6a7510e0126f40/drivers/overlay/overlay.go#L1084-L1089 WhiteoutFormat: archive.AUFSWhiteoutFormat, InUserNS: userns.RunningInUserNS(), diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go index ae89b86212..9ef1578395 100644 --- a/daemon/graphdriver/overlay/overlay.go +++ b/daemon/graphdriver/overlay/overlay.go @@ -50,9 +50,9 @@ type naiveDiffDriverWithApply struct { } // NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff. -func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver { +func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, idMap idtools.IdentityMapping) graphdriver.Driver { return &naiveDiffDriverWithApply{ - Driver: graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), + Driver: graphdriver.NewNaiveDiffDriver(driver, idMap), applyDiff: driver, } } @@ -99,8 +99,7 @@ type overlayOptions struct{} // Driver contains information about the home directory and the list of active mounts that are created using this driver. type Driver struct { home string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping ctr *graphdriver.RefCounter supportsDType bool locker *locker.Locker @@ -115,7 +114,7 @@ func init() { // graphdriver.ErrNotSupported is returned. // If an overlay filesystem is not supported over an existing filesystem then // error graphdriver.ErrIncompatibleFS is returned. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { _, err := parseOptions(options) if err != nil { return nil, err @@ -156,13 +155,9 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } currentID := idtools.CurrentIdentity() - _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } dirID := idtools.Identity{ UID: currentID.UID, - GID: rootGID, + GID: idMap.RootPair().GID, } // Create the driver home dir @@ -171,14 +166,13 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } d := &Driver{ home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), supportsDType: supportsDType, locker: locker.New(), } - return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil + return NaiveDiffDriverWithApply(d, d.idMap), nil } func parseOptions(options []string) (*overlayOptions, error) { @@ -262,17 +256,12 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr } dir := d.dir(id) - - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return err - } - root := idtools.Identity{UID: rootUID, GID: rootGID} + root := d.idMap.RootPair() currentID := idtools.CurrentIdentity() dirID := idtools.Identity{ UID: currentID.UID, - GID: rootGID, + GID: root.GID, } if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { return err @@ -388,11 +377,8 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro if err != nil { return nil, err } - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return nil, err - } - if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + root := d.idMap.RootPair() + if err := idtools.MkdirAndChown(mergedDir, 0700, root); err != nil { return nil, err } var ( @@ -406,7 +392,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro } // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. - if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { + if err := root.Chown(path.Join(workDir, "work")); err != nil { return nil, err } return containerfs.NewLocalContainerFS(mergedDir), nil @@ -483,7 +469,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 return 0, err } - options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps} + options := &archive.TarOptions{IDMap: d.idMap} if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil { return 0, err } diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go index 71b785fcc7..0f18eaf20f 100644 --- a/daemon/graphdriver/overlay2/overlay.go +++ b/daemon/graphdriver/overlay2/overlay.go @@ -93,8 +93,7 @@ type overlayOptions struct { // mounts that are created using this driver. type Driver struct { home string - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping ctr *graphdriver.RefCounter quotaCtl *quota.Control options overlayOptions @@ -124,7 +123,7 @@ func init() { // graphdriver.ErrNotSupported is returned. // If an overlay filesystem is not supported over an existing filesystem then // the error graphdriver.ErrIncompatibleFS is returned. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { opts, err := parseOptions(options) if err != nil { return nil, err @@ -164,15 +163,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) } - _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } - cur := idtools.CurrentIdentity() dirID := idtools.Identity{ UID: cur.UID, - GID: rootGID, + GID: idMap.RootPair().GID, } if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { return nil, err @@ -183,15 +177,14 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d := &Driver{ home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), supportsDType: supportsDType, locker: locker.New(), options: *opts, } - d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) + d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, idMap) if backingFs == "xfs" { // Try to enable project quota support over xfs. @@ -351,14 +344,10 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { dir := d.dir(id) - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return err - } - root := idtools.Identity{UID: rootUID, GID: rootGID} + root := d.idMap.RootPair() dirID := idtools.Identity{ UID: idtools.CurrentIdentity().UID, - GID: rootGID, + GID: root.GID, } if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil { @@ -580,11 +569,8 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e mount := unix.Mount mountTarget := mergedDir - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return nil, err - } - if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + root := d.idMap.RootPair() + if err := idtools.MkdirAndChown(mergedDir, 0700, root); err != nil { return nil, err } @@ -618,7 +604,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e if !readonly { // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. - if err := os.Chown(path.Join(workDir, workDirName), rootUID, rootGID); err != nil { + if err := root.Chown(path.Join(workDir, workDirName)); err != nil { return nil, err } } @@ -702,8 +688,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 logger.Debugf("Applying tar in %s", applyDir) // Overlay doesn't need the parent id to apply the diff if err := untar(diff, applyDir, &archive.TarOptions{ - UIDMaps: d.uidMaps, - GIDMaps: d.gidMaps, + IDMap: d.idMap, WhiteoutFormat: archive.OverlayWhiteoutFormat, }); err != nil { return 0, err @@ -740,8 +725,7 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { logger.Debugf("Tar with options on %s", diffPath) return archive.TarWithOptions(diffPath, &archive.TarOptions{ Compression: archive.Uncompressed, - UIDMaps: d.uidMaps, - GIDMaps: d.gidMaps, + IDMap: d.idMap, WhiteoutFormat: archive.OverlayWhiteoutFormat, }) } diff --git a/daemon/graphdriver/plugin.go b/daemon/graphdriver/plugin.go index 00f1dc9b6d..33108371bf 100644 --- a/daemon/graphdriver/plugin.go +++ b/daemon/graphdriver/plugin.go @@ -51,5 +51,5 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options) return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", pt)) } - return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps) + return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.IDMap) } diff --git a/daemon/graphdriver/proxy.go b/daemon/graphdriver/proxy.go index cb350d8074..022808de34 100644 --- a/daemon/graphdriver/proxy.go +++ b/daemon/graphdriver/proxy.go @@ -38,13 +38,12 @@ type graphDriverResponse struct { } type graphDriverInitRequest struct { - Home string - Opts []string `json:"Opts"` - UIDMaps []idtools.IDMap `json:"UIDMaps"` - GIDMaps []idtools.IDMap `json:"GIDMaps"` + Home string + Opts []string `json:"Opts"` + idtools.IdentityMapping } -func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []idtools.IDMap) error { +func (d *graphDriverProxy) Init(home string, opts []string, idMap idtools.IdentityMapping) error { if !d.p.IsV1() { if cp, ok := d.p.(plugingetter.CountedPlugin); ok { // always acquire here, it will be cleaned up on daemon shutdown @@ -52,10 +51,9 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i } } args := &graphDriverInitRequest{ - Home: home, - Opts: opts, - UIDMaps: uidMaps, - GIDMaps: gidMaps, + Home: home, + Opts: opts, + IdentityMapping: idMap, } var ret graphDriverResponse if err := d.client.Call("GraphDriver.Init", args, &ret); err != nil { diff --git a/daemon/graphdriver/proxy_test.go b/daemon/graphdriver/proxy_test.go new file mode 100644 index 0000000000..9d0a84e7a9 --- /dev/null +++ b/daemon/graphdriver/proxy_test.go @@ -0,0 +1,43 @@ +package graphdriver // import "github.com/docker/docker/daemon/graphdriver" + +import ( + "encoding/json" + "testing" + + "github.com/docker/docker/pkg/idtools" + "gotest.tools/v3/assert" +) + +func TestGraphDriverInitRequestIsCompatible(t *testing.T) { + // Graph driver plugins may unmarshal into this version of the init + // request struct. Verify that the serialization of + // graphDriverInitRequest is fully backwards compatible. + + type graphDriverInitRequestV1 struct { + Home string + Opts []string `json:"Opts"` + UIDMaps []idtools.IDMap `json:"UIDMaps"` + GIDMaps []idtools.IDMap `json:"GIDMaps"` + } + + args := graphDriverInitRequest{ + Home: "homedir", + Opts: []string{"option1", "option2"}, + IdentityMapping: idtools.IdentityMapping{ + UIDMaps: []idtools.IDMap{{ContainerID: 123, HostID: 456, Size: 42}}, + GIDMaps: []idtools.IDMap{{ContainerID: 789, HostID: 1011, Size: 16}}, + }, + } + v, err := json.Marshal(&args) + assert.NilError(t, err) + + var got graphDriverInitRequestV1 + assert.NilError(t, json.Unmarshal(v, &got)) + want := graphDriverInitRequestV1{ + Home: args.Home, + Opts: args.Opts, + UIDMaps: args.UIDMaps, + GIDMaps: args.GIDMaps, + } + assert.DeepEqual(t, got, want) +} diff --git a/daemon/graphdriver/vfs/copy_unsupported.go b/daemon/graphdriver/vfs/copy_unsupported.go index 1b0185fedb..92aac565ab 100644 --- a/daemon/graphdriver/vfs/copy_unsupported.go +++ b/daemon/graphdriver/vfs/copy_unsupported.go @@ -3,8 +3,11 @@ package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" -import "github.com/docker/docker/pkg/chrootarchive" +import ( + "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/idtools" +) func dirCopy(srcDir, dstDir string) error { - return chrootarchive.NewArchiver(nil).CopyWithTar(srcDir, dstDir) + return chrootarchive.NewArchiver(idtools.IdentityMapping{}).CopyWithTar(srcDir, dstDir) } diff --git a/daemon/graphdriver/vfs/driver.go b/daemon/graphdriver/vfs/driver.go index 7b61d2c227..2c6c720eab 100644 --- a/daemon/graphdriver/vfs/driver.go +++ b/daemon/graphdriver/vfs/driver.go @@ -27,23 +27,19 @@ func init() { // Init returns a new VFS driver. // 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, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { d := &Driver{ home: home, - idMapping: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), + idMapping: idMap, } if err := d.parseOptions(options); err != nil { return nil, err } - _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } dirID := idtools.Identity{ UID: idtools.CurrentIdentity().UID, - GID: rootGID, + GID: d.idMapping.RootPair().GID, } if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { return nil, err @@ -55,7 +51,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, quota.ErrQuotaNotSupported } - return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil + return graphdriver.NewNaiveDiffDriver(d, d.idMapping), nil } // Driver holds information about the driver, home directory of the driver. @@ -65,7 +61,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap type Driver struct { driverQuota home string - idMapping *idtools.IdentityMapping + idMapping idtools.IdentityMapping } func (d *Driver) String() string { diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index a384f8cde1..1e3b9343ee 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -97,7 +97,7 @@ type Driver struct { } // InitFilter returns a new Windows storage filter driver. -func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func InitFilter(home string, options []string, _ idtools.IdentityMapping) (graphdriver.Driver, error) { logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) fsType, err := getFileSystemType(string(home[0])) diff --git a/daemon/graphdriver/zfs/zfs.go b/daemon/graphdriver/zfs/zfs.go index 8abff324bd..e438ab0ab1 100644 --- a/daemon/graphdriver/zfs/zfs.go +++ b/daemon/graphdriver/zfs/zfs.go @@ -47,7 +47,7 @@ func (*Logger) Log(cmd []string) { // Init returns a new ZFS driver. // It takes base mount path and an array of options which are represented as key value pairs. // Each option is in the for key=value. 'zfs.fsname' is expected to be a valid key in the options. -func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(base string, opt []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { var err error logger := logrus.WithField("storage-driver", "zfs") @@ -106,14 +106,9 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName) } - _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } - dirID := idtools.Identity{ UID: idtools.CurrentIdentity().UID, - GID: rootGID, + GID: idMap.RootPair().GID, } if err := idtools.MkdirAllAndChown(base, 0710, dirID); err != nil { return nil, fmt.Errorf("Failed to create '%s': %v", base, err) @@ -123,12 +118,11 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri dataset: rootDataset, options: options, filesystemsCache: filesystemsCache, - uidMaps: uidMaps, - gidMaps: gidMaps, + idMap: idMap, ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), locker: locker.New(), } - return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil + return graphdriver.NewNaiveDiffDriver(d, idMap), nil } func parseOptions(opt []string) (zfsOptions, error) { @@ -181,8 +175,7 @@ type Driver struct { options zfsOptions sync.Mutex // protects filesystem cache against concurrent access filesystemsCache map[string]bool - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + idMap idtools.IdentityMapping ctr *graphdriver.RefCounter locker *locker.Locker } @@ -395,12 +388,9 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e options := label.FormatMountLabel("", mountLabel) logrus.WithField("storage-driver", "zfs").Debugf(`mount("%s", "%s", "%s")`, filesystem, mountpoint, options) - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) - if err != nil { - return nil, err - } + root := d.idMap.RootPair() // Create the target directories if they don't exist - if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(mountpoint, 0755, root); err != nil { return nil, err } @@ -410,7 +400,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e // this could be our first mount after creation of the filesystem, and the root dir may still have root // permissions instead of the remapped root uid:gid (if user namespaces are enabled): - if err := os.Chown(mountpoint, rootUID, rootGID); err != nil { + if err := root.Chown(mountpoint); err != nil { return nil, fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err) } diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 51b8778333..be3a4a61ae 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -227,13 +227,13 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts { userNS := false // user if c.HostConfig.UsernsMode.IsPrivate() { - uidMap := daemon.idMapping.UIDs() + uidMap := daemon.idMapping.UIDMaps if uidMap != nil { userNS = true ns := specs.LinuxNamespace{Type: "user"} setNamespace(s, ns) s.Linux.UIDMappings = specMapping(uidMap) - s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs()) + s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDMaps) } } // network @@ -689,7 +689,7 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts { // TODO: until a kernel/mount solution exists for handling remount in a user namespace, // we must clear the readonly flag for the cgroups mount (@mrunalp concurs) - if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged { + if uidMap := daemon.idMapping.UIDMaps; uidMap != nil || c.HostConfig.Privileged { for i, m := range s.Mounts { if m.Type == "cgroup" { clearReadOnly(&s.Mounts[i]) diff --git a/daemon/oci_linux_test.go b/daemon/oci_linux_test.go index 6e60d07f7c..42084c900d 100644 --- a/daemon/oci_linux_test.go +++ b/daemon/oci_linux_test.go @@ -11,7 +11,6 @@ import ( "github.com/docker/docker/daemon/network" "github.com/docker/docker/libnetwork" "github.com/docker/docker/pkg/containerfs" - "github.com/docker/docker/pkg/idtools" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" @@ -31,7 +30,6 @@ func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon { d := &Daemon{ // some empty structs to avoid getting a panic // caused by a null pointer dereference - idMapping: &idtools.IdentityMapping{}, configStore: &config.Config{}, linkIndex: newLinkIndex(), netController: netController, diff --git a/integration/image/remove_unix_test.go b/integration/image/remove_unix_test.go index ed24e276ff..046fa8532e 100644 --- a/integration/image/remove_unix_test.go +++ b/integration/image/remove_unix_test.go @@ -49,7 +49,7 @@ func TestRemoveImageGarbageCollector(t *testing.T) { MetadataStorePathTemplate: filepath.Join(d.RootDir(), "image", "%s", "layerdb"), GraphDriver: d.StorageDriver(), GraphDriverOptions: nil, - IDMapping: &idtools.IdentityMapping{}, + IDMapping: idtools.IdentityMapping{}, PluginGetter: nil, ExperimentalEnabled: false, }) diff --git a/integration/plugin/graphdriver/external_test.go b/integration/plugin/graphdriver/external_test.go index 57fb27856d..cb261774b8 100644 --- a/integration/plugin/graphdriver/external_test.go +++ b/integration/plugin/graphdriver/external_test.go @@ -19,6 +19,7 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/requirement" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/testutil/daemon" "gotest.tools/v3/assert" @@ -146,9 +147,9 @@ func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mu base, err := os.MkdirTemp("", name) assert.NilError(t, err) - vfsProto, err := vfs.Init(base, []string{}, nil, nil) + vfsProto, err := vfs.Init(base, []string{}, idtools.IdentityMapping{}) assert.NilError(t, err, "error initializing graph driver") - driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil) + driver := graphdriver.NewNaiveDiffDriver(vfsProto, idtools.IdentityMapping{}) ec[ext] = &graphEventsCounter{} mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { diff --git a/layer/layer_store.go b/layer/layer_store.go index 090b7cf7c8..0e8f567c1f 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -48,7 +48,7 @@ type StoreOptions struct { MetadataStorePathTemplate string GraphDriver string GraphDriverOptions []string - IDMapping *idtools.IdentityMapping + IDMapping idtools.IdentityMapping PluginGetter plugingetter.PluginGetter ExperimentalEnabled bool } @@ -58,8 +58,7 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) { driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{ Root: options.Root, DriverOptions: options.GraphDriverOptions, - UIDMaps: options.IDMapping.UIDs(), - GIDMaps: options.IDMapping.GIDs(), + IDMap: options.IDMapping, ExperimentalEnabled: options.ExperimentalEnabled, }) if err != nil { diff --git a/layer/layer_test.go b/layer/layer_test.go index 25ccfeb354..710f7423ad 100644 --- a/layer/layer_test.go +++ b/layer/layer_test.go @@ -41,7 +41,7 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) { }, } - options := graphdriver.Options{Root: td, UIDMaps: uidMap, GIDMaps: gidMap} + options := graphdriver.Options{Root: td, IDMap: idtools.IdentityMapping{UIDMaps: uidMap, GIDMaps: gidMap}} return graphdriver.GetDriver("vfs", nil, options) } diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index a9fd1e9552..986f014491 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -40,8 +40,7 @@ type ( ExcludePatterns []string Compression Compression NoLchown bool - UIDMaps []idtools.IDMap - GIDMaps []idtools.IDMap + IDMap idtools.IdentityMapping ChownOpts *idtools.Identity IncludeSourceDir bool // WhiteoutFormat is the expected on disk format for whiteout files. @@ -63,12 +62,12 @@ type ( // mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations. type Archiver struct { Untar func(io.Reader, string, *TarOptions) error - IDMapping *idtools.IdentityMapping + IDMapping idtools.IdentityMapping } // NewDefaultArchiver returns a new Archiver without any IdentityMapping func NewDefaultArchiver() *Archiver { - return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}} + return &Archiver{Untar: Untar} } // breakoutError is used to differentiate errors related to breaking out @@ -534,7 +533,7 @@ type tarAppender struct { // for hardlink mapping SeenFiles map[uint64]string - IdentityMapping *idtools.IdentityMapping + IdentityMapping idtools.IdentityMapping ChownOpts *idtools.Identity // For packing and unpacking whiteout files in the @@ -544,7 +543,7 @@ type tarAppender struct { WhiteoutConverter tarWhiteoutConverter } -func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender { +func newTarAppender(idMapping idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender { return &tarAppender{ SeenFiles: make(map[uint64]string), TarWriter: tar.NewWriter(writer), @@ -860,7 +859,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) go func() { ta := newTarAppender( - idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps), + options.IDMap, compressWriter, options.ChownOpts, ) @@ -1044,8 +1043,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err defer pools.BufioReader32KPool.Put(trBuf) var dirs []*tar.Header - idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) - rootIDs := idMapping.RootPair() + rootIDs := options.IDMap.RootPair() whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) if err != nil { return err @@ -1134,7 +1132,7 @@ loop: } trBuf.Reset(tr) - if err := remapIDs(idMapping, hdr); err != nil { + if err := remapIDs(options.IDMap, hdr); err != nil { return err } @@ -1221,8 +1219,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error { } defer archive.Close() options := &TarOptions{ - UIDMaps: archiver.IDMapping.UIDs(), - GIDMaps: archiver.IDMapping.GIDs(), + IDMap: archiver.IDMapping, } return archiver.Untar(archive, dst, options) } @@ -1235,8 +1232,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error { } defer archive.Close() options := &TarOptions{ - UIDMaps: archiver.IDMapping.UIDs(), - GIDMaps: archiver.IDMapping.GIDs(), + IDMap: archiver.IDMapping, } return archiver.Untar(archive, dst, options) } @@ -1343,11 +1339,11 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { } // IdentityMapping returns the IdentityMapping of the archiver. -func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping { +func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping { return archiver.IDMapping } -func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error { +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 return err diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index fed4ff685d..87f54406a9 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -791,7 +791,7 @@ func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) { expectedGID int }{ {&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: 100001, GID: 100001}, IDMap: idtools.IdentityMapping{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}, diff --git a/pkg/archive/changes.go b/pkg/archive/changes.go index a0f25942c1..9ad7d7efb8 100644 --- a/pkg/archive/changes.go +++ b/pkg/archive/changes.go @@ -394,10 +394,10 @@ func ChangesSize(newDir string, changes []Change) int64 { } // ExportChanges produces an Archive from the provided changes, relative to dir. -func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) { +func ExportChanges(dir string, changes []Change, idMap idtools.IdentityMapping) (io.ReadCloser, error) { reader, writer := io.Pipe() go func() { - ta := newTarAppender(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil) + ta := newTarAppender(idMap, writer, nil) // this buffer is needed for the duration of this piped stream defer pools.BufioWriter32KPool.Put(ta.Buffer) diff --git a/pkg/archive/changes_posix_test.go b/pkg/archive/changes_posix_test.go index 4c71cb2575..cdf854c438 100644 --- a/pkg/archive/changes_posix_test.go +++ b/pkg/archive/changes_posix_test.go @@ -8,6 +8,8 @@ import ( "path" "sort" "testing" + + "github.com/docker/docker/pkg/idtools" ) func TestHardLinkOrder(t *testing.T) { @@ -60,7 +62,7 @@ func TestHardLinkOrder(t *testing.T) { sort.Sort(changesByPath(changes)) // ExportChanges - ar, err := ExportChanges(dest, changes, nil, nil) + ar, err := ExportChanges(dest, changes, idtools.IdentityMapping{}) if err != nil { t.Fatal(err) } @@ -72,7 +74,7 @@ func TestHardLinkOrder(t *testing.T) { // reverse sort sort.Sort(sort.Reverse(changesByPath(changes))) // ExportChanges - arRev, err := ExportChanges(dest, changes, nil, nil) + arRev, err := ExportChanges(dest, changes, idtools.IdentityMapping{}) if err != nil { t.Fatal(err) } diff --git a/pkg/archive/changes_test.go b/pkg/archive/changes_test.go index 81b87aa12b..5d818e105c 100644 --- a/pkg/archive/changes_test.go +++ b/pkg/archive/changes_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/Microsoft/hcsshim/osversion" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/system" "gotest.tools/v3/assert" @@ -444,7 +445,7 @@ func TestApplyLayer(t *testing.T) { changes, err := ChangesDirs(dst, src) assert.NilError(t, err) - layer, err := ExportChanges(dst, changes, nil, nil) + layer, err := ExportChanges(dst, changes, idtools.IdentityMapping{}) assert.NilError(t, err) layerCopy, err := NewTempArchive(layer, "") diff --git a/pkg/archive/diff.go b/pkg/archive/diff.go index 6174bc2af4..f83d126faf 100644 --- a/pkg/archive/diff.go +++ b/pkg/archive/diff.go @@ -9,7 +9,6 @@ import ( "runtime" "strings" - "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" "github.com/sirupsen/logrus" @@ -32,7 +31,6 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, if options.ExcludePatterns == nil { options.ExcludePatterns = []string{} } - idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) aufsTempdir := "" aufsHardlinks := make(map[string]*tar.Header) @@ -192,7 +190,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, srcData = tmpFile } - if err := remapIDs(idMapping, srcHdr); err != nil { + if err := remapIDs(options.IDMap, srcHdr); err != nil { return 0, err } diff --git a/pkg/chrootarchive/archive.go b/pkg/chrootarchive/archive.go index 427abee7e8..656355f978 100644 --- a/pkg/chrootarchive/archive.go +++ b/pkg/chrootarchive/archive.go @@ -20,10 +20,7 @@ func init() { } // NewArchiver returns a new Archiver which uses chrootarchive.Untar -func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver { - if idMapping == nil { - idMapping = &idtools.IdentityMapping{} - } +func NewArchiver(idMapping idtools.IdentityMapping) *archive.Archiver { return &archive.Archiver{ Untar: Untar, IDMapping: idMapping, @@ -76,8 +73,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions // If dest is inside a root then directory is created within chroot by extractor. // This case is only currently used by cp. if dest == root { - idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) - rootIDs := idMapping.RootPair() + rootIDs := options.IDMap.RootPair() dest = filepath.Clean(dest) if _, err := os.Stat(dest); os.IsNotExist(err) { diff --git a/pkg/chrootarchive/archive_test.go b/pkg/chrootarchive/archive_test.go index c96c83b072..d00769c54a 100644 --- a/pkg/chrootarchive/archive_test.go +++ b/pkg/chrootarchive/archive_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" "gotest.tools/v3/skip" @@ -22,7 +23,7 @@ func init() { reexec.Init() } -var chrootArchiver = NewArchiver(nil) +var chrootArchiver = NewArchiver(idtools.IdentityMapping{}) func TarUntar(src, dst string) error { return chrootArchiver.TarUntar(src, dst) diff --git a/pkg/containerfs/archiver.go b/pkg/containerfs/archiver.go index a47d7db760..d5f153b373 100644 --- a/pkg/containerfs/archiver.go +++ b/pkg/containerfs/archiver.go @@ -26,7 +26,7 @@ type Archiver struct { DstDriver Driver Tar TarFunc Untar UntarFunc - IDMapping *idtools.IdentityMapping + IDMapping idtools.IdentityMapping } // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other. @@ -39,8 +39,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error { } defer tarArchive.Close() options := &archive.TarOptions{ - UIDMaps: archiver.IDMapping.UIDs(), - GIDMaps: archiver.IDMapping.GIDs(), + IDMap: archiver.IDMapping, } return archiver.Untar(tarArchive, dst, options) } @@ -53,8 +52,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error { } defer tarArchive.Close() options := &archive.TarOptions{ - UIDMaps: archiver.IDMapping.UIDs(), - GIDMaps: archiver.IDMapping.GIDs(), + IDMap: archiver.IDMapping, } return archiver.Untar(tarArchive, dst, options) } @@ -181,11 +179,11 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) { } // IdentityMapping returns the IdentityMapping of the archiver. -func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping { +func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping { return archiver.IDMapping } -func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error { +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 return err diff --git a/pkg/idtools/idtools.go b/pkg/idtools/idtools.go index 25a57b231e..1e0a89004a 100644 --- a/pkg/idtools/idtools.go +++ b/pkg/idtools/idtools.go @@ -108,70 +108,72 @@ type Identity struct { SID string } -// IdentityMapping contains a mappings of UIDs and GIDs -type IdentityMapping struct { - uids []IDMap - gids []IDMap +// Chown changes the numeric uid and gid of the named file to id.UID and id.GID. +func (id Identity) Chown(name string) error { + return os.Chown(name, id.UID, id.GID) } -// NewIDMappingsFromMaps creates a new mapping from two slices -// Deprecated: this is a temporary shim while transitioning to IDMapping -func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping { - return &IdentityMapping{uids: uids, gids: gids} +// IdentityMapping contains a mappings of UIDs and GIDs. +// The zero value represents an empty mapping. +type IdentityMapping struct { + UIDMaps []IDMap `json:"UIDMaps"` + GIDMaps []IDMap `json:"GIDMaps"` } // 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 // and gid maps are empty. -func (i *IdentityMapping) RootPair() Identity { - uid, gid, _ := GetRootUIDGID(i.uids, i.gids) +func (i IdentityMapping) RootPair() Identity { + uid, gid, _ := GetRootUIDGID(i.UIDMaps, i.GIDMaps) return Identity{UID: uid, GID: 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 -func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) { +func (i IdentityMapping) ToHost(pair Identity) (Identity, error) { var err error target := i.RootPair() if pair.UID != target.UID { - target.UID, err = toHost(pair.UID, i.uids) + target.UID, err = toHost(pair.UID, i.UIDMaps) if err != nil { return target, err } } if pair.GID != target.GID { - target.GID, err = toHost(pair.GID, i.gids) + target.GID, err = toHost(pair.GID, i.GIDMaps) } return target, err } // ToContainer returns the container UID and GID for the host uid and gid -func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) { - uid, err := toContainer(pair.UID, i.uids) +func (i IdentityMapping) ToContainer(pair Identity) (int, int, error) { + uid, err := toContainer(pair.UID, i.UIDMaps) if err != nil { return -1, -1, err } - gid, err := toContainer(pair.GID, i.gids) + gid, err := toContainer(pair.GID, i.GIDMaps) return uid, gid, err } // Empty returns true if there are no id mappings -func (i *IdentityMapping) Empty() bool { - return len(i.uids) == 0 && len(i.gids) == 0 +func (i IdentityMapping) Empty() bool { + return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0 } -// UIDs return the UID mapping -// TODO: remove this once everything has been refactored to use pairs -func (i *IdentityMapping) UIDs() []IDMap { - return i.uids +// UIDs returns the mapping for UID. +// +// Deprecated: reference the UIDMaps field directly. +func (i IdentityMapping) UIDs() []IDMap { + return i.UIDMaps } -// GIDs return the UID mapping -// TODO: remove this once everything has been refactored to use pairs -func (i *IdentityMapping) GIDs() []IDMap { - return i.gids +// GIDs returns the mapping for GID. +// +// Deprecated: reference the GIDMaps field directly. +func (i IdentityMapping) GIDs() []IDMap { + return i.GIDMaps } func createIDMap(subidRanges ranges) []IDMap { diff --git a/pkg/idtools/idtools_unix.go b/pkg/idtools/idtools_unix.go index ceec0339b5..7a7ccc3e42 100644 --- a/pkg/idtools/idtools_unix.go +++ b/pkg/idtools/idtools_unix.go @@ -240,24 +240,37 @@ func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT // NewIdentityMapping takes a requested username and // using the data from /etc/sub{uid,gid} ranges, creates the // proper uid and gid remapping ranges for that user/group pair +// +// Deprecated: Use LoadIdentityMapping. func NewIdentityMapping(name string) (*IdentityMapping, error) { + m, err := LoadIdentityMapping(name) + if err != nil { + return nil, err + } + return &m, err +} + +// LoadIdentityMapping takes a requested username and +// using the data from /etc/sub{uid,gid} ranges, creates the +// proper uid and gid remapping ranges for that user/group pair +func LoadIdentityMapping(name string) (IdentityMapping, error) { usr, err := LookupUser(name) if err != nil { - return nil, fmt.Errorf("Could not get user for username %s: %v", name, err) + return IdentityMapping{}, fmt.Errorf("Could not get user for username %s: %v", name, err) } subuidRanges, err := lookupSubUIDRanges(usr) if err != nil { - return nil, err + return IdentityMapping{}, err } subgidRanges, err := lookupSubGIDRanges(usr) if err != nil { - return nil, err + return IdentityMapping{}, err } - return &IdentityMapping{ - uids: subuidRanges, - gids: subgidRanges, + return IdentityMapping{ + UIDMaps: subuidRanges, + GIDMaps: subgidRanges, }, nil } diff --git a/pkg/idtools/idtools_unix_test.go b/pkg/idtools/idtools_unix_test.go index c43ed6fc8d..162d02578f 100644 --- a/pkg/idtools/idtools_unix_test.go +++ b/pkg/idtools/idtools_unix_test.go @@ -321,10 +321,10 @@ func TestNewIDMappings(t *testing.T) { tempUser, err := user.Lookup(tempUser) assert.Check(t, err) - idMapping, err := NewIdentityMapping(tempUser.Username) + idMapping, err := LoadIdentityMapping(tempUser.Username) assert.Check(t, err) - rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDs(), idMapping.GIDs()) + rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDMaps, idMapping.GIDMaps) assert.Check(t, err) dirName, err := os.MkdirTemp("", "mkdirall")