diff --git a/storage/cmd/containers-storage/create.go b/storage/cmd/containers-storage/create.go index 4d89a8b87e..0e68630dab 100644 --- a/storage/cmd/containers-storage/create.go +++ b/storage/cmd/containers-storage/create.go @@ -122,7 +122,12 @@ func createLayer(flags *mflag.FlagSet, action string, m storage.Store, args []st if err != nil { return 1, err } - options := &storage.LayerOptions{IDMappingOptions: *mappings} + options := &storage.LayerOptions{IDMappingOptions: storage.LayerIDMappingOptions{ + HostUIDMapping: mappings.HostUIDMapping, + HostGIDMapping: mappings.HostGIDMapping, + UIDMap: mappings.UIDMap, + GIDMap: mappings.GIDMap, + }} layer, err := m.CreateLayer(paramID, parent, paramNames, paramMountLabel, !paramCreateRO, options) if err != nil { return 1, err @@ -155,7 +160,12 @@ func importLayer(flags *mflag.FlagSet, action string, m storage.Store, args []st if err != nil { return 1, err } - options := &storage.LayerOptions{IDMappingOptions: *mappings} + options := &storage.LayerOptions{IDMappingOptions: storage.LayerIDMappingOptions{ + HostUIDMapping: mappings.HostUIDMapping, + HostGIDMapping: mappings.HostGIDMapping, + UIDMap: mappings.UIDMap, + GIDMap: mappings.GIDMap, + }} layer, _, err := m.PutLayer(paramID, parent, paramNames, paramMountLabel, !paramCreateRO, options, diffStream) if err != nil { return 1, err diff --git a/storage/containers.go b/storage/containers.go index ebdd19c599..5319df3759 100644 --- a/storage/containers.go +++ b/storage/containers.go @@ -76,9 +76,12 @@ type Container struct { // is set before using it. Created time.Time `json:"created"` - // UIDMap and GIDMap are used for setting up a container's root - // filesystem for use inside of a user namespace where UID mapping is - // being used. + // UIDMap and GIDMap are the caller's requested UID/GID mapping for this + // container's user namespace. They always reflect what the caller + // asked for, regardless of whether the mapping was applied at layer + // creation time (chown) or is deferred to mount time (idmapped mounts). + // At mount time, these maps are passed to the graph driver so that the + // container sees the expected file ownership. UIDMap []idtools.IDMap `json:"uidmap,omitempty"` GIDMap []idtools.IDMap `json:"gidmap,omitempty"` diff --git a/storage/drivers/driver.go b/storage/drivers/driver.go index 1ca50b6462..4eab783aa9 100644 --- a/storage/drivers/driver.go +++ b/storage/drivers/driver.go @@ -95,9 +95,6 @@ type MountOpts struct { // Volatile specifies whether the container storage can be optimized // at the cost of not syncing all the dirty files in memory. Volatile bool - - // DisableShifting forces the driver to not do any ID shifting at runtime. - DisableShifting bool } // ApplyDiffOpts contains optional arguments for ApplyDiff methods. diff --git a/storage/drivers/overlay/overlay.go b/storage/drivers/overlay/overlay.go index b50319707b..ec1d163009 100644 --- a/storage/drivers/overlay/overlay.go +++ b/storage/drivers/overlay/overlay.go @@ -1504,7 +1504,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO readWrite := !inAdditionalStore - if !d.SupportsShifting(options.UidMaps, options.GidMaps) || options.DisableShifting { + if !d.SupportsShifting(options.UidMaps, options.GidMaps) { disableShifting = true } diff --git a/storage/layers.go b/storage/layers.go index d176e456c1..a1040981df 100644 --- a/storage/layers.go +++ b/storage/layers.go @@ -167,8 +167,11 @@ type Layer struct { // Flags is arbitrary data about the layer. Flags map[string]any `json:"flags,omitempty"` - // UIDMap and GIDMap are used for setting up a layer's contents - // for use inside of a user namespace where UID mapping is being used. + // UIDMap and GIDMap are the on-disk ID mappings for this layer: the + // chown mapping that was applied to the layer's files at creation + // time. When the driver supports shifting (idmapped mounts), no + // chown occurs and these fields are empty. The caller's requested + // mapping is applied at mount time instead (see Container.UIDMap/GIDMap). UIDMap []idtools.IDMap `json:"uidmap,omitempty"` GIDMap []idtools.IDMap `json:"gidmap,omitempty"` @@ -1596,8 +1599,8 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount UIDs: templateUIDs, GIDs: templateGIDs, Flags: newMapFrom(moreOptions.Flags), - UIDMap: copySlicePreferringNil(moreOptions.UIDMap), - GIDMap: copySlicePreferringNil(moreOptions.GIDMap), + UIDMap: copySlicePreferringNil(moreOptions.IDMappingOptions.UIDMap), + GIDMap: copySlicePreferringNil(moreOptions.IDMappingOptions.GIDMap), BigDataNames: []string{}, location: r.pickStoreLocation(moreOptions.Volatile, writeable), } @@ -1641,7 +1644,10 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount } } - idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap) + idMappings := idtools.NewIDMappingsFromMaps(moreOptions.IDMappingOptions.UIDMap, moreOptions.IDMappingOptions.GIDMap) + if moreOptions.IDMappingOptions.HostUIDMapping && moreOptions.IDMappingOptions.HostGIDMapping { + idMappings = &idtools.IDMappings{} + } opts := drivers.CreateOpts{ MountLabel: mountLabel, StorageOpt: options, @@ -2597,7 +2603,7 @@ func (r *layerStore) stageWithUnlockedStore(sl *maybeStagedLayerExtraction, pare result, err := applyDiff(layerOptions, sl.diff, f, func(payload io.Reader) (int64, error) { cleanup, stagedLayer, size, err := sl.staging.StartStagingDiffToApply(parent, drivers.ApplyDiffOpts{ Diff: payload, - Mappings: idtools.NewIDMappingsFromMaps(layerOptions.UIDMap, layerOptions.GIDMap), + Mappings: idtools.NewIDMappingsFromMaps(layerOptions.IDMappingOptions.UIDMap, layerOptions.IDMappingOptions.GIDMap), // MountLabel is not supported for the unlocked extraction, see the comment in (*store).PutLayer() MountLabel: "", }) diff --git a/storage/store.go b/storage/store.go index 36ffbafe49..ef087957bd 100644 --- a/storage/store.go +++ b/storage/store.go @@ -633,13 +633,39 @@ type AutoUserNsOptions = types.AutoUserNsOptions type IDMappingOptions = types.IDMappingOptions +// LayerIDMappingOptions are the on-disk ID mappings for a layer. +// +// Unlike the caller-facing IDMappingOptions (which expresses what mapping +// the caller wants), these record how files are actually stored. The two +// may differ: when the graph driver supports shifting, no chown +// occurs so HostUIDMapping/HostGIDMapping are true and UIDMap/GIDMap +// are empty, even though the caller requested a non-trivial mapping. +// The caller's requested mapping is still honored at mount time via +// the Container's UIDMap/GIDMap. +type LayerIDMappingOptions struct { + // HostUIDMapping is true when files in this layer are stored with host + // UIDs. + HostUIDMapping bool + // HostGIDMapping is true when files in this layer are stored with host + // GIDs. See HostUIDMapping for details. + HostGIDMapping bool + // UIDMap is the on-disk UID mapping: it records the chown that was + // applied to the layer's files at creation time. Empty when + // HostUIDMapping is true. + UIDMap []idtools.IDMap + // GIDMap is the on-disk GID mapping: it records the chown that was + // applied to the layer's files at creation time. Empty when + // HostGIDMapping is true. + GIDMap []idtools.IDMap +} + // LayerOptions is used for passing options to a Store's CreateLayer() and PutLayer() methods. type LayerOptions struct { // IDMappingOptions specifies the type of ID mapping which should be // used for this layer. If nothing is specified, the layer will // inherit settings from its parent layer or, if it has no parent // layer, the Store object. - types.IDMappingOptions + IDMappingOptions LayerIDMappingOptions // TemplateLayer is the ID of a layer whose contents will be used to // initialize this layer. If set, it should be a child of the layer // which we want to use as the parent of the new layer. @@ -708,10 +734,18 @@ type ImageBigDataOption struct { // ContainerOptions is used for passing options to a Store's CreateContainer() method. type ContainerOptions struct { - // IDMappingOptions specifies the type of ID mapping which should be - // used for this container's layer. If nothing is specified, the - // container's layer will inherit settings from the image's top layer - // or, if it is not being created based on an image, the Store object. + // IDMappingOptions specifies the caller's desired ID mapping for the + // container's user namespace. + // + // These express what the caller wants, not what ends up on disk. + // The store records them in the Container and uses them at mount + // time. How the layer's files are stored depends on whether the + // driver supports shifting: if it does, no chown occurs and the + // mapping is applied at mount time; otherwise files are chowned at + // layer creation time. + // + // If nothing is specified, mappings are inherited from the image's top + // layer or, if no image, from the Store's defaults. types.IDMappingOptions LabelOpts []string // Flags is a set of named flags and their values to store with the container. @@ -1518,14 +1552,14 @@ func populateLayerOptions(s *store, rlstore rwLayerStore, rlstores []roLayerStor options.BigData = slices.Clone(lOptions.BigData) options.Flags = copyMapPreferringNil(lOptions.Flags) } - if options.HostUIDMapping { - options.UIDMap = nil + if options.IDMappingOptions.HostUIDMapping { + options.IDMappingOptions.UIDMap = nil } - if options.HostGIDMapping { - options.GIDMap = nil + if options.IDMappingOptions.HostGIDMapping { + options.IDMappingOptions.GIDMap = nil } - uidMap := options.UIDMap - gidMap := options.GIDMap + uidMap := options.IDMappingOptions.UIDMap + gidMap := options.IDMappingOptions.GIDMap if parent != "" { var err error parentLayer, unlock, err = getParentLayer(rlstore, rlstores, parent) @@ -1546,26 +1580,26 @@ func populateLayerOptions(s *store, rlstore rwLayerStore, rlstores []roLayerStor return nil, nil, unlock, ErrParentIsContainer } } - if !options.HostUIDMapping && len(options.UIDMap) == 0 { + if !options.IDMappingOptions.HostUIDMapping && len(options.IDMappingOptions.UIDMap) == 0 { uidMap = parentLayer.UIDMap } - if !options.HostGIDMapping && len(options.GIDMap) == 0 { + if !options.IDMappingOptions.HostGIDMapping && len(options.IDMappingOptions.GIDMap) == 0 { gidMap = parentLayer.GIDMap } } else { - if !options.HostUIDMapping && len(options.UIDMap) == 0 { + if !options.IDMappingOptions.HostUIDMapping && len(options.IDMappingOptions.UIDMap) == 0 { uidMap = s.uidMap } - if !options.HostGIDMapping && len(options.GIDMap) == 0 { + if !options.IDMappingOptions.HostGIDMapping && len(options.IDMappingOptions.GIDMap) == 0 { gidMap = s.gidMap } } if s.canUseShifting(uidMap, gidMap) { - options.IDMappingOptions = types.IDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil} + options.IDMappingOptions = LayerIDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil} } else { - options.IDMappingOptions = types.IDMappingOptions{ - HostUIDMapping: options.HostUIDMapping, - HostGIDMapping: options.HostGIDMapping, + options.IDMappingOptions = LayerIDMappingOptions{ + HostUIDMapping: options.IDMappingOptions.HostUIDMapping, + HostGIDMapping: options.IDMappingOptions.HostGIDMapping, UIDMap: copySlicePreferringNil(uidMap), GIDMap: copySlicePreferringNil(gidMap), } @@ -1856,14 +1890,14 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore roImageStore, rlst // mappings, and register it as an alternate top layer in the image. var layerOptions LayerOptions if s.canUseShifting(options.UIDMap, options.GIDMap) { - layerOptions.IDMappingOptions = types.IDMappingOptions{ + layerOptions.IDMappingOptions = LayerIDMappingOptions{ HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil, } } else { - layerOptions.IDMappingOptions = types.IDMappingOptions{ + layerOptions.IDMappingOptions = LayerIDMappingOptions{ HostUIDMapping: options.HostUIDMapping, HostGIDMapping: options.HostGIDMapping, UIDMap: copySlicePreferringNil(options.UIDMap), @@ -2008,20 +2042,12 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat // But in transient store mode, all container layers are volatile. Volatile: options.Volatile || s.transientStore, } - if s.canUseShifting(uidMap, gidMap) { - layerOptions.IDMappingOptions = types.IDMappingOptions{ - HostUIDMapping: true, - HostGIDMapping: true, - UIDMap: nil, - GIDMap: nil, - } - } else { - layerOptions.IDMappingOptions = types.IDMappingOptions{ - HostUIDMapping: idMappingsOptions.HostUIDMapping, - HostGIDMapping: idMappingsOptions.HostGIDMapping, - UIDMap: copySlicePreferringNil(uidMap), - GIDMap: copySlicePreferringNil(gidMap), - } + useHostMapping := idMappingsOptions.HostUIDMapping || s.canUseShifting(uidMap, gidMap) + layerOptions.IDMappingOptions = LayerIDMappingOptions{ + HostUIDMapping: useHostMapping, + HostGIDMapping: useHostMapping, + UIDMap: copySlicePreferringNil(uidMap), + GIDMap: copySlicePreferringNil(gidMap), } if options.Flags == nil { options.Flags = make(map[string]any) @@ -3074,10 +3100,6 @@ func (s *store) Mount(id, mountLabel string) (string, error) { if err != nil { return "", err } - if options.UidMaps != nil || options.GidMaps != nil { - options.DisableShifting = !s.canUseShifting(options.UidMaps, options.GidMaps) - } - if err := rlstore.startWriting(); err != nil { return "", err } diff --git a/storage/tests/helpers.bash b/storage/tests/helpers.bash index 1d0646716f..928e17d24a 100755 --- a/storage/tests/helpers.bash +++ b/storage/tests/helpers.bash @@ -21,9 +21,6 @@ function setup() { TESTDIR=${BATS_TMPDIR}/tmp.${suffix} rm -fr ${TESTDIR} mkdir -p ${TESTDIR}/{root,runroot} - # disable idmapped mounts in the overlay driver, since that - # is the expectation in the idmaps.bats tests. - export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes } # Teardown the basic storage setup diff --git a/storage/tests/idmaps.bats b/storage/tests/idmaps.bats index a340360f72..3bf89f85d6 100644 --- a/storage/tests/idmaps.bats +++ b/storage/tests/idmaps.bats @@ -23,6 +23,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 @@ -137,6 +138,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 # Create some temporary files. @@ -231,6 +233,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 # Create some temporary files. @@ -375,6 +378,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 # Create some temporary files. for i in $(seq $n) ; do @@ -440,6 +444,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 # Create some temporary files. @@ -614,6 +619,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 filelist= @@ -799,6 +805,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 # Create some temporary files. @@ -968,28 +975,207 @@ load helpers ;; esac - # Create a base layer. - run storage --debug=false create-layer + # Check if the driver supports shifting (idmapped mounts). + run storage --debug=false status + if ! grep -q "Supports shifting: true" <<< "$output" ; then + skip "driver does not support shifting" + fi + + n=5 + + # Create some temporary files with different ownerships. + for i in $(seq $n) ; do + createrandom "$TESTDIR"/file$i + chown ${i}:${i} "$TESTDIR"/file$i + done + + # Select UID/GID ranges for the container mappings. + for i in $(seq $n) ; do + uidrange[$i]=$((($RANDOM+32767)*65536)) + gidrange[$i]=$((($RANDOM+32767)*65536)) + done + + # Create a base layer with host mappings. + run storage --debug=false create-layer --hostuidmap --hostgidmap echo "$output" [ "$status" -eq 0 ] [ "$output" != "" ] baselayer="$output" - # Create the lower layer. - run storage --debug=false create-layer $baselayer + + # Create the lower layer with host mappings and populate it. + run storage --debug=false create-layer --hostuidmap --hostgidmap $baselayer [ "$status" -eq 0 ] [ "$output" != "" ] lowerlayer="$output" - # Mount the layer. + run storage --debug=false mount $lowerlayer [ "$status" -eq 0 ] [ "$output" != "" ] lowermount="$output" - # Put a file in the layer. - createrandom "$lowermount"/file + + # Copy files in with their original ownership. + for i in $(seq $n) ; do + cp -p "$TESTDIR"/file$i ${lowermount} + done storage unmount $lowerlayer + # Create an image from this layer. imagename=idmappedimage-shifting storage create-image --name=$imagename $lowerlayer + + # Create containers with different UID/GID mappings and verify + # that files appear with the host-mapped ownership via idmapped mounts. + for i in $(seq $n) ; do + run storage --debug=false create-container --uidmap 0:${uidrange[$i]}:$(($n+1)) --gidmap 0:${gidrange[$i]}:$(($n+1)) $imagename + echo "$output" + [ "$status" -eq 0 ] + [ "$output" != "" ] + container=${lines[0]} + + run storage --debug=false mount "$container" + echo "$output" + [ "$status" -eq 0 ] + [ "$output" != "" ] + mount="$output" + + for j in $(seq $n) ; do + ownerids=$(stat -c %u:%g ${mount}/file$j) + expected=$((${uidrange[$i]}+$j)):$((${gidrange[$i]}+$j)) + echo "file$j: on-disk IDs: $ownerids, expected IDs: $expected" + [ "$ownerids" = "$expected" ] + done + run storage --debug=false unmount "$container" + [ "$status" -eq 0 ] + done + + # Also verify that a container with host mappings sees the original ownership. + run storage --debug=false create-container --hostuidmap --hostgidmap $imagename + [ "$status" -eq 0 ] + [ "$output" != "" ] + hostcontainer=${lines[0]} + + run storage --debug=false mount "$hostcontainer" + [ "$status" -eq 0 ] + [ "$output" != "" ] + hostmount="$output" + + for j in $(seq $n) ; do + ownerids=$(stat -c %u:%g ${hostmount}/file$j) + echo "file$j: on-disk IDs: $ownerids, expected IDs: $j:$j" + [ "$ownerids" = "$j:$j" ] + done + run storage --debug=false unmount "$hostcontainer" + [ "$status" -eq 0 ] +} + +@test "idmaps-shifting-image-mount-uses-host-mappings" { + if [ "$OS" != "Linux" ]; then + skip "not supported on $OS" + fi + case "$STORAGE_DRIVER" in + overlay*) + ;; + *) + skip "not supported by driver $STORAGE_DRIVER" + ;; + esac + case "$STORAGE_OPTION" in + *mount_program*) + skip "test not supported when using mount_program" + ;; + esac + + # Check if the driver supports shifting (idmapped mounts). + run storage --debug=false status + if ! grep -q "Supports shifting: true" <<< "$output" ; then + skip "driver does not support shifting" + fi + + n=3 + + # Create some temporary files with different ownerships. + for i in $(seq $n) ; do + createrandom "$TESTDIR"/file$i + chown ${i}:${i} "$TESTDIR"/file$i + done + + # Build a chain of layers, each created with a different UID/GID mapping. + # With shifting enabled, UpdateLayerIDMap should not be called, so files + # remain with their original on-disk ownership. + + # Layer 1 (base): host mappings, contains file1. + run storage --debug=false create-layer --hostuidmap --hostgidmap + [ "$status" -eq 0 ] + [ "$output" != "" ] + layer1="$output" + + run storage --debug=false mount $layer1 + [ "$status" -eq 0 ] + mount1="$output" + cp -p "$TESTDIR"/file1 ${mount1} + storage unmount $layer1 + + # Layer 2: different mapping, contains file2. + uidrange2=$((($RANDOM+32767)*65536)) + gidrange2=$((($RANDOM+32767)*65536)) + run storage --debug=false create-layer --uidmap 0:${uidrange2}:65536 --gidmap 0:${gidrange2}:65536 $layer1 + [ "$status" -eq 0 ] + [ "$output" != "" ] + layer2="$output" + + run storage --debug=false mount $layer2 + [ "$status" -eq 0 ] + mount2="$output" + cp "$TESTDIR"/file2 ${mount2} + chown ${uidrange2}:${gidrange2} ${mount2}/file2 + storage unmount $layer2 + + # Layer 3: yet another mapping, contains file3. + uidrange3=$((($RANDOM+32767)*65536)) + gidrange3=$((($RANDOM+32767)*65536)) + run storage --debug=false create-layer --uidmap 0:${uidrange3}:65536 --gidmap 0:${gidrange3}:65536 $layer2 + [ "$status" -eq 0 ] + [ "$output" != "" ] + layer3="$output" + + run storage --debug=false mount $layer3 + [ "$status" -eq 0 ] + mount3="$output" + cp "$TESTDIR"/file3 ${mount3} + chown ${uidrange3}:${gidrange3} ${mount3}/file3 + storage unmount $layer3 + + # Create an image from the top layer. + imagename=shifting-multilayer-image + storage create-image --name=$imagename $layer3 + + # Mount the image read-only and verify all files appear with host mappings. + # The image mount should not apply any UID/GID shifting: files must appear + # with their original on-disk ownership. + run storage --debug=false mount -r $imagename + [ "$status" -eq 0 ] + [ "$output" != "" ] + imgmount="$output" + + # file1 was stored as 1:1 (host mapping). + run stat -c %u:%g ${imgmount}/file1 + echo "file1: $output, expected 1:1" + [ "$output" = "1:1" ] + + # file2 was stored with uidrange2 ownership on disk, which maps to + # container UID 0. When mounted with host mappings, it must show + # the on-disk ownership. + run stat -c %u:%g ${imgmount}/file2 + echo "file2: $output, expected ${uidrange2}:${gidrange2}" + [ "$output" = "${uidrange2}:${gidrange2}" ] + + # file3 was stored with uidrange3 ownership on disk. + run stat -c %u:%g ${imgmount}/file3 + echo "file3: $output, expected ${uidrange3}:${gidrange3}" + [ "$output" = "${uidrange3}:${gidrange3}" ] + + run storage umount $imagename + [ "$status" -eq 0 ] } @test "idmaps-create-layer-from-another-image-store" { @@ -1013,6 +1199,7 @@ load helpers skip "test not supported when using mount_program" ;; esac + export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes n=5 host=2 diff --git a/storage/types/idmappings.go b/storage/types/idmappings.go index e53ec229c3..4ed73b88dd 100644 --- a/storage/types/idmappings.go +++ b/storage/types/idmappings.go @@ -28,21 +28,34 @@ type AutoUserNsOptions struct { AdditionalGIDMappings []idtools.IDMap } -// IDMappingOptions are used for specifying how ID mapping should be set up for -// a layer or container. +// IDMappingOptions specifies the caller's desired UID/GID mapping for a +// layer or container. +// +// These options express what the caller wants, the mapping that the +// container's user namespace should use. They do not describe what is +// stored on disk. Depending on the graph driver, the store may apply +// the mapping at layer creation time (by chowning files) or defer it to +// mount time (using idmapped mounts or fuse-overlayfs options), but +// that distinction is transparent to the caller. +// +// The resolution order for the effective UID/GID maps is: +// 1. If HostUIDMapping/HostGIDMapping is true, no mapping is used (the +// corresponding UIDMap/GIDMap is ignored and treated as empty). +// 2. If UIDMap/GIDMap contain at least one entry, those mappings are used. +// 3. Otherwise, if the layer has a parent, the parent's mappings are inherited. +// 4. Otherwise, the Store-level default mappings are used. type IDMappingOptions struct { - // UIDMap and GIDMap are used for setting up a layer's root filesystem - // for use inside of a user namespace where ID mapping is being used. - // If HostUIDMapping/HostGIDMapping is true, no mapping of the - // respective type will be used. Otherwise, if UIDMap and/or GIDMap - // contain at least one mapping, one or both will be used. By default, - // if neither of those conditions apply, if the layer has a parent - // layer, the parent layer's mapping will be used, and if it does not - // have a parent layer, the mapping which was passed to the Store - // object when it was initialized will be used. + // HostUIDMapping indicates that no UID mapping should be applied. + // When true, UIDMap is ignored and files are accessed with host UIDs. HostUIDMapping bool + // HostGIDMapping indicates that no GID mapping should be applied. + // When true, GIDMap is ignored and files are accessed with host GIDs. HostGIDMapping bool - UIDMap []idtools.IDMap + // UIDMap defines the UID mappings for the user namespace. + // Only used when HostUIDMapping is false. + UIDMap []idtools.IDMap + // GIDMap defines the GID mappings for the user namespace. + // Only used when HostGIDMapping is false. GIDMap []idtools.IDMap AutoUserNs bool AutoUserNsOpts AutoUserNsOptions diff --git a/storage/userns.go b/storage/userns.go index 86ca8b5938..38047dba2f 100644 --- a/storage/userns.go +++ b/storage/userns.go @@ -187,7 +187,7 @@ outer: } layerOptions := &LayerOptions{ - IDMappingOptions: types.IDMappingOptions{ + IDMappingOptions: LayerIDMappingOptions{ HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil,