From 098a44c07f38a14147a57feffa31b551a47f3c73 Mon Sep 17 00:00:00 2001 From: Cory Snider Date: Mon, 14 Mar 2022 15:24:29 -0400 Subject: [PATCH] Finish refactor of UID/GID usage to a new struct Finish the refactor which was partially completed with commit 34536c498d56, passing around IdentityMapping structs instead of pairs of []IDMap slices. Existing code which uses []IDMap relies on zero-valued fields to be valid, empty mappings. So in order to successfully finish the refactoring without introducing bugs, their replacement therefore also needs to have a useful zero value which represents an empty mapping. Change IdentityMapping to be a pass-by-value type so that there are no nil pointers to worry about. The functionality provided by the deprecated NewIDMappingsFromMaps function is required by unit tests to to construct arbitrary IdentityMapping values. And the daemon will always need to access the mappings to pass them to the Linux kernel. Accommodate these use cases by exporting the struct fields instead. BuildKit currently depends on the UIDs and GIDs methods so we cannot get rid of them yet. Signed-off-by: Cory Snider --- .../adapters/snapshot/snapshot.go | 18 +++++-- builder/builder-next/builder.go | 6 +-- builder/builder-next/executor_unix.go | 11 +++- builder/builder-next/executor_windows.go | 2 +- builder/dockerfile/builder.go | 8 +-- builder/dockerfile/internals.go | 2 +- builder/dockerfile/internals_linux.go | 2 +- builder/dockerfile/internals_linux_test.go | 8 +-- builder/dockerfile/internals_windows.go | 2 +- contrib/docker-device-tool/device_tool.go | 3 +- daemon/archive_tarcopyoptions.go | 3 +- daemon/daemon.go | 4 +- daemon/daemon_unix.go | 16 +++--- daemon/daemon_windows.go | 6 +-- daemon/export.go | 3 +- daemon/graphdriver/aufs/aufs.go | 28 +++------- daemon/graphdriver/aufs/aufs_test.go | 3 +- daemon/graphdriver/btrfs/btrfs.go | 24 ++++----- daemon/graphdriver/devmapper/deviceset.go | 20 ++----- .../graphdriver/devmapper/devmapper_test.go | 3 +- daemon/graphdriver/devmapper/driver.go | 28 ++++------ daemon/graphdriver/driver.go | 17 +++--- daemon/graphdriver/fsdiff.go | 13 ++--- .../fuse-overlayfs/fuseoverlayfs.go | 46 +++++----------- daemon/graphdriver/overlay/overlay.go | 40 +++++--------- daemon/graphdriver/overlay2/overlay.go | 40 +++++--------- daemon/graphdriver/plugin.go | 2 +- daemon/graphdriver/proxy.go | 16 +++--- daemon/graphdriver/proxy_test.go | 43 +++++++++++++++ daemon/graphdriver/vfs/copy_unsupported.go | 7 ++- daemon/graphdriver/vfs/driver.go | 14 ++--- daemon/graphdriver/windows/windows.go | 2 +- daemon/graphdriver/zfs/zfs.go | 26 +++------ daemon/oci_linux.go | 6 +-- daemon/oci_linux_test.go | 2 - integration/image/remove_unix_test.go | 2 +- .../plugin/graphdriver/external_test.go | 5 +- layer/layer_store.go | 5 +- layer/layer_test.go | 2 +- pkg/archive/archive.go | 28 +++++----- pkg/archive/archive_test.go | 2 +- pkg/archive/changes.go | 4 +- pkg/archive/changes_posix_test.go | 6 ++- pkg/archive/changes_test.go | 3 +- pkg/archive/diff.go | 4 +- pkg/chrootarchive/archive.go | 8 +-- pkg/chrootarchive/archive_test.go | 3 +- pkg/containerfs/archiver.go | 12 ++--- pkg/idtools/idtools.go | 54 ++++++++++--------- pkg/idtools/idtools_unix.go | 25 ++++++--- pkg/idtools/idtools_unix_test.go | 4 +- 51 files changed, 301 insertions(+), 340 deletions(-) create mode 100644 daemon/graphdriver/proxy_test.go diff --git a/builder/builder-next/adapters/snapshot/snapshot.go b/builder/builder-next/adapters/snapshot/snapshot.go index 28075f4f6e..8b01647dc0 100644 --- a/builder/builder-next/adapters/snapshot/snapshot.go +++ b/builder/builder-next/adapters/snapshot/snapshot.go @@ -31,7 +31,7 @@ type Opt struct { GraphDriver graphdriver.Driver LayerStore layer.Store Root string - IdentityMapping *idtools.IdentityMapping + IdentityMapping idtools.IdentityMapping } type graphIDRegistrar interface { @@ -99,7 +99,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 { @@ -481,7 +486,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) { @@ -526,5 +531,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 5ee9d8e30d..ae52b89f53 100644 --- a/builder/builder-next/builder.go +++ b/builder/builder-next/builder.go @@ -72,7 +72,7 @@ type Opt struct { RegistryHosts docker.RegistryHosts BuilderConfig config.BuilderConfig Rootless bool - IdentityMapping *idtools.IdentityMapping + IdentityMapping idtools.IdentityMapping DNSConfig config.DNSConfig ApparmorProfile string } @@ -90,10 +90,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 f42bb1bc63..deb11777f4 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 198423140b..1c6f7cefb0 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -97,7 +97,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 @@ -1460,7 +1460,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 e5c6dbbc5a..24c20f0d2a 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1063,7 +1063,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()) } @@ -1162,9 +1162,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 @@ -1172,25 +1172,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 6ea232da06..d97471a5d4 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -62,7 +62,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 } @@ -436,8 +436,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 a446c2b7b7..25be5fdc70 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 0b1b46b0c8..b9d27eb966 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -49,7 +49,7 @@ type StoreOptions struct { MetadataStorePathTemplate string GraphDriver string GraphDriverOptions []string - IDMapping *idtools.IdentityMapping + IDMapping idtools.IdentityMapping PluginGetter plugingetter.PluginGetter ExperimentalEnabled bool } @@ -59,8 +59,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")