Skip to content

Commit 911af70

Browse files
authored
Merge pull request #734 from giuseppe/fix-map-handling
storage: fix UID/GID map handling for shifting layers
2 parents 498ccd1 + 599dbf6 commit 911af70

10 files changed

Lines changed: 312 additions & 77 deletions

File tree

storage/cmd/containers-storage/create.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ func createLayer(flags *mflag.FlagSet, action string, m storage.Store, args []st
122122
if err != nil {
123123
return 1, err
124124
}
125-
options := &storage.LayerOptions{IDMappingOptions: *mappings}
125+
options := &storage.LayerOptions{IDMappingOptions: storage.LayerIDMappingOptions{
126+
HostUIDMapping: mappings.HostUIDMapping,
127+
HostGIDMapping: mappings.HostGIDMapping,
128+
UIDMap: mappings.UIDMap,
129+
GIDMap: mappings.GIDMap,
130+
}}
126131
layer, err := m.CreateLayer(paramID, parent, paramNames, paramMountLabel, !paramCreateRO, options)
127132
if err != nil {
128133
return 1, err
@@ -155,7 +160,12 @@ func importLayer(flags *mflag.FlagSet, action string, m storage.Store, args []st
155160
if err != nil {
156161
return 1, err
157162
}
158-
options := &storage.LayerOptions{IDMappingOptions: *mappings}
163+
options := &storage.LayerOptions{IDMappingOptions: storage.LayerIDMappingOptions{
164+
HostUIDMapping: mappings.HostUIDMapping,
165+
HostGIDMapping: mappings.HostGIDMapping,
166+
UIDMap: mappings.UIDMap,
167+
GIDMap: mappings.GIDMap,
168+
}}
159169
layer, _, err := m.PutLayer(paramID, parent, paramNames, paramMountLabel, !paramCreateRO, options, diffStream)
160170
if err != nil {
161171
return 1, err

storage/containers.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@ type Container struct {
7676
// is set before using it.
7777
Created time.Time `json:"created"`
7878

79-
// UIDMap and GIDMap are used for setting up a container's root
80-
// filesystem for use inside of a user namespace where UID mapping is
81-
// being used.
79+
// UIDMap and GIDMap are the caller's requested UID/GID mapping for this
80+
// container's user namespace. They always reflect what the caller
81+
// asked for, regardless of whether the mapping was applied at layer
82+
// creation time (chown) or is deferred to mount time (idmapped mounts).
83+
// At mount time, these maps are passed to the graph driver so that the
84+
// container sees the expected file ownership.
8285
UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
8386
GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
8487

storage/drivers/driver.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ type MountOpts struct {
9595
// Volatile specifies whether the container storage can be optimized
9696
// at the cost of not syncing all the dirty files in memory.
9797
Volatile bool
98-
99-
// DisableShifting forces the driver to not do any ID shifting at runtime.
100-
DisableShifting bool
10198
}
10299

103100
// ApplyDiffOpts contains optional arguments for ApplyDiff methods.

storage/drivers/overlay/overlay.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
15041504

15051505
readWrite := !inAdditionalStore
15061506

1507-
if !d.SupportsShifting(options.UidMaps, options.GidMaps) || options.DisableShifting {
1507+
if !d.SupportsShifting(options.UidMaps, options.GidMaps) {
15081508
disableShifting = true
15091509
}
15101510

storage/layers.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,11 @@ type Layer struct {
167167
// Flags is arbitrary data about the layer.
168168
Flags map[string]any `json:"flags,omitempty"`
169169

170-
// UIDMap and GIDMap are used for setting up a layer's contents
171-
// for use inside of a user namespace where UID mapping is being used.
170+
// UIDMap and GIDMap are the on-disk ID mappings for this layer: the
171+
// chown mapping that was applied to the layer's files at creation
172+
// time. When the driver supports shifting (idmapped mounts), no
173+
// chown occurs and these fields are empty. The caller's requested
174+
// mapping is applied at mount time instead (see Container.UIDMap/GIDMap).
172175
UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
173176
GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
174177

@@ -1600,8 +1603,8 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
16001603
UIDs: templateUIDs,
16011604
GIDs: templateGIDs,
16021605
Flags: newMapFrom(moreOptions.Flags),
1603-
UIDMap: copySlicePreferringNil(moreOptions.UIDMap),
1604-
GIDMap: copySlicePreferringNil(moreOptions.GIDMap),
1606+
UIDMap: copySlicePreferringNil(moreOptions.IDMappingOptions.UIDMap),
1607+
GIDMap: copySlicePreferringNil(moreOptions.IDMappingOptions.GIDMap),
16051608
BigDataNames: []string{},
16061609
location: r.pickStoreLocation(moreOptions.Volatile, writeable),
16071610
}
@@ -1645,7 +1648,10 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
16451648
}
16461649
}
16471650

1648-
idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap)
1651+
idMappings := idtools.NewIDMappingsFromMaps(moreOptions.IDMappingOptions.UIDMap, moreOptions.IDMappingOptions.GIDMap)
1652+
if moreOptions.IDMappingOptions.HostUIDMapping && moreOptions.IDMappingOptions.HostGIDMapping {
1653+
idMappings = &idtools.IDMappings{}
1654+
}
16491655
opts := drivers.CreateOpts{
16501656
MountLabel: mountLabel,
16511657
StorageOpt: options,
@@ -2601,7 +2607,7 @@ func (r *layerStore) stageWithUnlockedStore(sl *maybeStagedLayerExtraction, pare
26012607
result, err := applyDiff(layerOptions, sl.diff, f, func(payload io.Reader) (int64, error) {
26022608
cleanup, stagedLayer, size, err := sl.staging.StartStagingDiffToApply(parent, drivers.ApplyDiffOpts{
26032609
Diff: payload,
2604-
Mappings: idtools.NewIDMappingsFromMaps(layerOptions.UIDMap, layerOptions.GIDMap),
2610+
Mappings: idtools.NewIDMappingsFromMaps(layerOptions.IDMappingOptions.UIDMap, layerOptions.IDMappingOptions.GIDMap),
26052611
// MountLabel is not supported for the unlocked extraction, see the comment in (*store).PutLayer()
26062612
MountLabel: "",
26072613
})

storage/store.go

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -633,13 +633,39 @@ type AutoUserNsOptions = types.AutoUserNsOptions
633633

634634
type IDMappingOptions = types.IDMappingOptions
635635

636+
// LayerIDMappingOptions are the on-disk ID mappings for a layer.
637+
//
638+
// Unlike the caller-facing IDMappingOptions (which expresses what mapping
639+
// the caller wants), these record how files are actually stored. The two
640+
// may differ: when the graph driver supports shifting, no chown
641+
// occurs so HostUIDMapping/HostGIDMapping are true and UIDMap/GIDMap
642+
// are empty, even though the caller requested a non-trivial mapping.
643+
// The caller's requested mapping is still honored at mount time via
644+
// the Container's UIDMap/GIDMap.
645+
type LayerIDMappingOptions struct {
646+
// HostUIDMapping is true when files in this layer are stored with host
647+
// UIDs.
648+
HostUIDMapping bool
649+
// HostGIDMapping is true when files in this layer are stored with host
650+
// GIDs. See HostUIDMapping for details.
651+
HostGIDMapping bool
652+
// UIDMap is the on-disk UID mapping: it records the chown that was
653+
// applied to the layer's files at creation time. Empty when
654+
// HostUIDMapping is true.
655+
UIDMap []idtools.IDMap
656+
// GIDMap is the on-disk GID mapping: it records the chown that was
657+
// applied to the layer's files at creation time. Empty when
658+
// HostGIDMapping is true.
659+
GIDMap []idtools.IDMap
660+
}
661+
636662
// LayerOptions is used for passing options to a Store's CreateLayer() and PutLayer() methods.
637663
type LayerOptions struct {
638664
// IDMappingOptions specifies the type of ID mapping which should be
639665
// used for this layer. If nothing is specified, the layer will
640666
// inherit settings from its parent layer or, if it has no parent
641667
// layer, the Store object.
642-
types.IDMappingOptions
668+
IDMappingOptions LayerIDMappingOptions
643669
// TemplateLayer is the ID of a layer whose contents will be used to
644670
// initialize this layer. If set, it should be a child of the layer
645671
// which we want to use as the parent of the new layer.
@@ -708,10 +734,18 @@ type ImageBigDataOption struct {
708734

709735
// ContainerOptions is used for passing options to a Store's CreateContainer() method.
710736
type ContainerOptions struct {
711-
// IDMappingOptions specifies the type of ID mapping which should be
712-
// used for this container's layer. If nothing is specified, the
713-
// container's layer will inherit settings from the image's top layer
714-
// or, if it is not being created based on an image, the Store object.
737+
// IDMappingOptions specifies the caller's desired ID mapping for the
738+
// container's user namespace.
739+
//
740+
// These express what the caller wants, not what ends up on disk.
741+
// The store records them in the Container and uses them at mount
742+
// time. How the layer's files are stored depends on whether the
743+
// driver supports shifting: if it does, no chown occurs and the
744+
// mapping is applied at mount time; otherwise files are chowned at
745+
// layer creation time.
746+
//
747+
// If nothing is specified, mappings are inherited from the image's top
748+
// layer or, if no image, from the Store's defaults.
715749
types.IDMappingOptions
716750
LabelOpts []string
717751
// 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
15181552
options.BigData = slices.Clone(lOptions.BigData)
15191553
options.Flags = copyMapPreferringNil(lOptions.Flags)
15201554
}
1521-
if options.HostUIDMapping {
1522-
options.UIDMap = nil
1555+
if options.IDMappingOptions.HostUIDMapping {
1556+
options.IDMappingOptions.UIDMap = nil
15231557
}
1524-
if options.HostGIDMapping {
1525-
options.GIDMap = nil
1558+
if options.IDMappingOptions.HostGIDMapping {
1559+
options.IDMappingOptions.GIDMap = nil
15261560
}
1527-
uidMap := options.UIDMap
1528-
gidMap := options.GIDMap
1561+
uidMap := options.IDMappingOptions.UIDMap
1562+
gidMap := options.IDMappingOptions.GIDMap
15291563
if parent != "" {
15301564
var err error
15311565
parentLayer, unlock, err = getParentLayer(rlstore, rlstores, parent)
@@ -1546,26 +1580,26 @@ func populateLayerOptions(s *store, rlstore rwLayerStore, rlstores []roLayerStor
15461580
return nil, nil, unlock, ErrParentIsContainer
15471581
}
15481582
}
1549-
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
1583+
if !options.IDMappingOptions.HostUIDMapping && len(options.IDMappingOptions.UIDMap) == 0 {
15501584
uidMap = parentLayer.UIDMap
15511585
}
1552-
if !options.HostGIDMapping && len(options.GIDMap) == 0 {
1586+
if !options.IDMappingOptions.HostGIDMapping && len(options.IDMappingOptions.GIDMap) == 0 {
15531587
gidMap = parentLayer.GIDMap
15541588
}
15551589
} else {
1556-
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
1590+
if !options.IDMappingOptions.HostUIDMapping && len(options.IDMappingOptions.UIDMap) == 0 {
15571591
uidMap = s.uidMap
15581592
}
1559-
if !options.HostGIDMapping && len(options.GIDMap) == 0 {
1593+
if !options.IDMappingOptions.HostGIDMapping && len(options.IDMappingOptions.GIDMap) == 0 {
15601594
gidMap = s.gidMap
15611595
}
15621596
}
15631597
if s.canUseShifting(uidMap, gidMap) {
1564-
options.IDMappingOptions = types.IDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil}
1598+
options.IDMappingOptions = LayerIDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil}
15651599
} else {
1566-
options.IDMappingOptions = types.IDMappingOptions{
1567-
HostUIDMapping: options.HostUIDMapping,
1568-
HostGIDMapping: options.HostGIDMapping,
1600+
options.IDMappingOptions = LayerIDMappingOptions{
1601+
HostUIDMapping: options.IDMappingOptions.HostUIDMapping,
1602+
HostGIDMapping: options.IDMappingOptions.HostGIDMapping,
15691603
UIDMap: copySlicePreferringNil(uidMap),
15701604
GIDMap: copySlicePreferringNil(gidMap),
15711605
}
@@ -1856,14 +1890,14 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore roImageStore, rlst
18561890
// mappings, and register it as an alternate top layer in the image.
18571891
var layerOptions LayerOptions
18581892
if s.canUseShifting(options.UIDMap, options.GIDMap) {
1859-
layerOptions.IDMappingOptions = types.IDMappingOptions{
1893+
layerOptions.IDMappingOptions = LayerIDMappingOptions{
18601894
HostUIDMapping: true,
18611895
HostGIDMapping: true,
18621896
UIDMap: nil,
18631897
GIDMap: nil,
18641898
}
18651899
} else {
1866-
layerOptions.IDMappingOptions = types.IDMappingOptions{
1900+
layerOptions.IDMappingOptions = LayerIDMappingOptions{
18671901
HostUIDMapping: options.HostUIDMapping,
18681902
HostGIDMapping: options.HostGIDMapping,
18691903
UIDMap: copySlicePreferringNil(options.UIDMap),
@@ -2008,20 +2042,12 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
20082042
// But in transient store mode, all container layers are volatile.
20092043
Volatile: options.Volatile || s.transientStore,
20102044
}
2011-
if s.canUseShifting(uidMap, gidMap) {
2012-
layerOptions.IDMappingOptions = types.IDMappingOptions{
2013-
HostUIDMapping: true,
2014-
HostGIDMapping: true,
2015-
UIDMap: nil,
2016-
GIDMap: nil,
2017-
}
2018-
} else {
2019-
layerOptions.IDMappingOptions = types.IDMappingOptions{
2020-
HostUIDMapping: idMappingsOptions.HostUIDMapping,
2021-
HostGIDMapping: idMappingsOptions.HostGIDMapping,
2022-
UIDMap: copySlicePreferringNil(uidMap),
2023-
GIDMap: copySlicePreferringNil(gidMap),
2024-
}
2045+
useHostMapping := idMappingsOptions.HostUIDMapping || s.canUseShifting(uidMap, gidMap)
2046+
layerOptions.IDMappingOptions = LayerIDMappingOptions{
2047+
HostUIDMapping: useHostMapping,
2048+
HostGIDMapping: useHostMapping,
2049+
UIDMap: copySlicePreferringNil(uidMap),
2050+
GIDMap: copySlicePreferringNil(gidMap),
20252051
}
20262052
if options.Flags == nil {
20272053
options.Flags = make(map[string]any)
@@ -3074,10 +3100,6 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
30743100
if err != nil {
30753101
return "", err
30763102
}
3077-
if options.UidMaps != nil || options.GidMaps != nil {
3078-
options.DisableShifting = !s.canUseShifting(options.UidMaps, options.GidMaps)
3079-
}
3080-
30813103
if err := rlstore.startWriting(); err != nil {
30823104
return "", err
30833105
}

storage/tests/helpers.bash

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ function setup() {
2121
TESTDIR=${BATS_TMPDIR}/tmp.${suffix}
2222
rm -fr ${TESTDIR}
2323
mkdir -p ${TESTDIR}/{root,runroot}
24-
# disable idmapped mounts in the overlay driver, since that
25-
# is the expectation in the idmaps.bats tests.
26-
export _CONTAINERS_OVERLAY_DISABLE_IDMAP=yes
2724
}
2825

2926
# Teardown the basic storage setup

0 commit comments

Comments
 (0)