diff --git a/docs/source/markdown/options/rootfs.md b/docs/source/markdown/options/rootfs.md index 1a773de8b1..6e4fba3388 100644 --- a/docs/source/markdown/options/rootfs.md +++ b/docs/source/markdown/options/rootfs.md @@ -21,3 +21,12 @@ finishes executing, similar to a tmpfs mount point being unmounted. Note: On **SELinux** systems, the rootfs needs the correct label, which is by default **unconfined_u:object_r:container_file_t:s0**. + + The `idmap` option if specified, creates an idmapped mount to the target user +namespace in the container. +The idmap option supports a custom mapping that can be different than the user +namespace used by the container. The mapping can be specified after the idmap +option like: `idmap=uids=0-1-10#10-11-10;gids=0-100-10`. For each triplet, the +first value is the start of the backing file system IDs that are mapped to the +second value on the host. The length of this mapping is given in the third value. +Multiple ranges are separated with #. diff --git a/go.mod b/go.mod index a708763d66..c3d910e664 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( github.com/chzyer/readline v1.5.1 // indirect github.com/containerd/cgroups v1.0.4 // indirect github.com/containerd/containerd v1.6.15 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.14.1 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/coreos/go-oidc/v3 v3.5.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect @@ -177,3 +177,5 @@ require ( ) replace github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.1-0.20220617142545-8b9452f75cbc + +replace github.com/containers/storage => github.com/containers/storage v1.45.3-0.20230131031022-998b8bf212eb diff --git a/go.sum b/go.sum index df6565c0a2..d602702f0b 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -68,7 +66,6 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= @@ -81,9 +78,7 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2 github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0= github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.4/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= @@ -238,10 +233,8 @@ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFY github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/stargz-snapshotter/estargz v0.12.0/go.mod h1:AIQ59TewBFJ4GOPEQXujcrJ/EKxh5xXZegW1rkR1P/M= -github.com/containerd/stargz-snapshotter/estargz v0.13.0 h1:fD7AwuVV+B40p0d9qVkH/Au1qhp8hn/HWJHIYjpEcfw= -github.com/containerd/stargz-snapshotter/estargz v0.13.0/go.mod h1:m+9VaGJGlhCnrcEUod8mYumTmRgblwd3rC5UCEh2Yp0= +github.com/containerd/stargz-snapshotter/estargz v0.14.1 h1:n9M2GDSWM96pyipFTA0DaU+zdtzi3Iwsnj/rIHr1yFM= +github.com/containerd/stargz-snapshotter/estargz v0.14.1/go.mod h1:uPtMw6ucGJYwImjhxk/oghZmfElF/841u86wReNggNk= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= @@ -283,10 +276,8 @@ github.com/containers/ocicrypt v1.1.7 h1:thhNr4fu2ltyGz8aMx8u48Ae0Pnbip3ePP9/mzk github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8NscCYRawuDNtw= github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY= github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc= -github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= -github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s= -github.com/containers/storage v1.45.3 h1:GbtTvTtp3GW2/tcFg5VhgHXcYMwVn2KfZKiHjf9FAOM= -github.com/containers/storage v1.45.3/go.mod h1:OdRUYHrq1HP6iAo79VxqtYuJzC5j4eA2I60jKOoCT7g= +github.com/containers/storage v1.45.3-0.20230131031022-998b8bf212eb h1:1N+upIrgGtvmLKJArMkVbHrGXmkSS8qADIP3RFwoJoI= +github.com/containers/storage v1.45.3-0.20230131031022-998b8bf212eb/go.mod h1:ikiIaFiVqg3QAtJWkh/VKQSdSlYE963sxwgHY5rIDu4= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= @@ -706,9 +697,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -877,7 +865,6 @@ github.com/opencontainers/runtime-tools v0.9.1-0.20221014010322-58c91d646d86/go. github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.2 h1:NFy2xCsjn7+WspbfZkUd5zyVeisV7VFbPSP96+8/ha4= @@ -1056,7 +1043,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1391,7 +1377,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/libpod/container_config.go b/libpod/container_config.go index 3720e110bf..84e1d3dcf3 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -116,6 +116,8 @@ type ContainerRootFSConfig struct { Rootfs string `json:"rootfs,omitempty"` // RootfsOverlay tells if rootfs has to be mounted as an overlay RootfsOverlay bool `json:"rootfs_overlay,omitempty"` + // RootfsMapping specifies if there are mappings to apply to the rootfs. + RootfsMapping *string `json:"rootfs_mapping,omitempty"` // ShmDir is the path to be mounted on /dev/shm in container. // If not set manually at creation time, Libpod will create a tmpfs // with the size specified in ShmSize and populate this with the path of diff --git a/libpod/container_internal.go b/libpod/container_internal.go index c37d6be2b1..607319c753 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -35,6 +35,7 @@ import ( "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idmap" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/mount" @@ -370,9 +371,6 @@ func (c *Container) syncContainer() error { } func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) { - if c.config.Rootfs != "" { - return - } *dest = *from // If we are creating a container inside a pod, we always want to inherit the // userns settings from the infra container. So clear the auto userns settings @@ -1525,9 +1523,34 @@ func (c *Container) mountStorage() (_ string, deferredErr error) { // We need to mount the container before volumes - to ensure the copyup // works properly. mountPoint := c.config.Rootfs + + if c.config.RootfsMapping != nil { + uidMappings, gidMappings, err := parseIDMapMountOption(c.config.IDMappings, *c.config.RootfsMapping, false) + if err != nil { + return "", err + } + + pid, cleanupFunc, err := idmap.CreateUsernsProcess(util.RuntimeSpecToIDtools(uidMappings), util.RuntimeSpecToIDtools(gidMappings)) + if err != nil { + return "", err + } + defer cleanupFunc() + + if err := idmap.CreateIDMappedMount(c.config.Rootfs, c.config.Rootfs, pid); err != nil { + return "", fmt.Errorf("failed to create idmapped mount: %w", err) + } + defer func() { + if deferredErr != nil { + if err := unix.Unmount(c.config.Rootfs, 0); err != nil { + logrus.Errorf("Unmounting idmapped rootfs for container %s after mount error: %v", c.ID(), err) + } + } + }() + } + // Check if overlay has to be created on top of Rootfs if c.config.RootfsOverlay { - overlayDest := c.runtime.RunRoot() + overlayDest := c.runtime.GraphRoot() contentDir, err := overlay.GenerateStructure(overlayDest, c.ID(), "rootfs", c.RootUID(), c.RootGID()) if err != nil { return "", fmt.Errorf("rootfs-overlay: failed to create TempDir in the %s directory: %w", overlayDest, err) @@ -1795,6 +1818,11 @@ func (c *Container) cleanupStorage() error { cleanupErr = err } } + if c.config.RootfsMapping != nil { + if err := unix.Unmount(c.config.Rootfs, 0); err != nil { + logrus.Errorf("Unmounting idmapped rootfs for container %s after mount error: %v", c.ID(), err) + } + } for _, containerMount := range c.config.Mounts { if err := c.unmountSHM(containerMount); err != nil { diff --git a/libpod/container_internal_common.go b/libpod/container_internal_common.go index f603c51dcf..a5d1fc047a 100644 --- a/libpod/container_internal_common.go +++ b/libpod/container_internal_common.go @@ -74,7 +74,7 @@ func parseOptionIDs(option string) ([]idtools.IDMap, error) { return ret, nil } -func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string) ([]spec.LinuxIDMapping, []spec.LinuxIDMapping, error) { +func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string, invert bool) ([]spec.LinuxIDMapping, []spec.LinuxIDMapping, error) { uidMap := idMappings.UIDMap gidMap := idMappings.GIDMap if strings.HasPrefix(option, "idmap=") { @@ -101,17 +101,33 @@ func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string) ([ uidMappings := make([]spec.LinuxIDMapping, len(uidMap)) gidMappings := make([]spec.LinuxIDMapping, len(gidMap)) for i, uidmap := range uidMap { - uidMappings[i] = spec.LinuxIDMapping{ - HostID: uint32(uidmap.ContainerID), - ContainerID: uint32(uidmap.HostID), - Size: uint32(uidmap.Size), + if invert { + uidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(uidmap.ContainerID), + ContainerID: uint32(uidmap.HostID), + Size: uint32(uidmap.Size), + } + } else { + uidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(uidmap.HostID), + ContainerID: uint32(uidmap.ContainerID), + Size: uint32(uidmap.Size), + } } } for i, gidmap := range gidMap { - gidMappings[i] = spec.LinuxIDMapping{ - HostID: uint32(gidmap.ContainerID), - ContainerID: uint32(gidmap.HostID), - Size: uint32(gidmap.Size), + if invert { + gidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(gidmap.ContainerID), + ContainerID: uint32(gidmap.HostID), + Size: uint32(gidmap.Size), + } + } else { + gidMappings[i] = spec.LinuxIDMapping{ + HostID: uint32(gidmap.HostID), + ContainerID: uint32(gidmap.ContainerID), + Size: uint32(gidmap.Size), + } } } return uidMappings, gidMappings, nil @@ -288,7 +304,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range m.Options { if o == "idmap" || strings.HasPrefix(o, "idmap=") { var err error - m.UIDMappings, m.GIDMappings, err = parseIDMapMountOption(c.config.IDMappings, o) + m.UIDMappings, m.GIDMappings, err = parseIDMapMountOption(c.config.IDMappings, o, true) if err != nil { return nil, err } diff --git a/libpod/container_internal_test.go b/libpod/container_internal_test.go index 167ffabe64..50103d944c 100644 --- a/libpod/container_internal_test.go +++ b/libpod/container_internal_test.go @@ -65,7 +65,7 @@ func TestParseIDMapMountOption(t *testing.T) { UIDMap: uidMap, GIDMap: gidMap, } - uids, gids, err := parseIDMapMountOption(options, "idmap") + uids, gids, err := parseIDMapMountOption(options, "idmap", true) assert.Nil(t, err) assert.Equal(t, len(uids), 1) assert.Equal(t, len(gids), 1) @@ -78,7 +78,7 @@ func TestParseIDMapMountOption(t *testing.T) { assert.Equal(t, gids[0].HostID, uint32(0)) assert.Equal(t, gids[0].Size, uint32(10000)) - uids, gids, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10") + uids, gids, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10", true) assert.Nil(t, err) assert.Equal(t, len(uids), 2) assert.Equal(t, len(gids), 1) @@ -95,19 +95,19 @@ func TestParseIDMapMountOption(t *testing.T) { assert.Equal(t, gids[0].HostID, uint32(0)) assert.Equal(t, gids[0].Size, uint32(10)) - _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10;foobar=bar") + _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10;foobar=bar", true) assert.NotNil(t, err) - _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12") + _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12", true) assert.NotNil(t, err) - _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12--12") + _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12--12", true) assert.NotNil(t, err) - _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#-1-12-12") + _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#-1-12-12", true) assert.NotNil(t, err) - _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0--12-0") + _, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0--12-0", true) assert.NotNil(t, err) } diff --git a/libpod/options.go b/libpod/options.go index d9a6f99765..3ce81ed6d3 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1334,7 +1334,7 @@ func WithCommand(command []string) CtrCreateOption { // WithRootFS sets the rootfs for the container. // This creates a container from a directory on disk and not an image. -func WithRootFS(rootfs string, overlay bool) CtrCreateOption { +func WithRootFS(rootfs string, overlay bool, mapping *string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return define.ErrCtrFinalized @@ -1344,6 +1344,7 @@ func WithRootFS(rootfs string, overlay bool) CtrCreateOption { } ctr.config.Rootfs = rootfs ctr.config.RootfsOverlay = overlay + ctr.config.RootfsMapping = mapping return nil } } diff --git a/libpod/runtime.go b/libpod/runtime.go index ece290f797..519c2d932a 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -973,6 +973,14 @@ func (r *Runtime) RunRoot() string { return r.store.RunRoot() } +// GraphRoot retrieves the current c/storage directory in use by Libpod. +func (r *Runtime) GraphRoot() string { + if r.store == nil { + return "" + } + return r.store.GraphRoot() +} + // GetName retrieves the name associated with a given full ID. // This works for both containers and pods, and does not distinguish between the // two. diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 635a0820c8..2e78267ef8 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -118,7 +118,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } if s.Rootfs != "" { - options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay)) + options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay, s.RootfsMapping)) } newImage, resolvedImageName, imageData, err := getImageFromSpec(ctx, rt, s) @@ -513,7 +513,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l options = append(options, libpod.WithShmSize(*s.ShmSize)) } if s.Rootfs != "" { - options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay)) + options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay, s.RootfsMapping)) } // Default used if not overridden on command line diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 2e7078115e..8905000248 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -238,6 +238,8 @@ type ContainerStorageConfig struct { Rootfs string `json:"rootfs,omitempty"` // RootfsOverlay tells if rootfs is actually an overlay on top of base path RootfsOverlay bool `json:"rootfs_overlay,omitempty"` + // RootfsMapping specifies if there are mappings to apply to the rootfs. + RootfsMapping *string `json:"rootfs_mapping,omitempty"` // ImageVolumeMode indicates how image volumes will be created. // Supported modes are "ignore" (do not create), "tmpfs" (create as // tmpfs), and "anonymous" (create as anonymous volumes). @@ -600,9 +602,15 @@ func NewSpecGenerator(arg string, rootfs bool) *SpecGenerator { csc.Rootfs = arg // check if rootfs should use overlay lastColonIndex := strings.LastIndex(csc.Rootfs, ":") - if lastColonIndex != -1 && lastColonIndex+1 < len(csc.Rootfs) && csc.Rootfs[lastColonIndex+1:] == "O" { - csc.RootfsOverlay = true - csc.Rootfs = csc.Rootfs[:lastColonIndex] + if lastColonIndex != -1 { + lastPart := csc.Rootfs[lastColonIndex+1:] + if lastPart == "O" { + csc.RootfsOverlay = true + csc.Rootfs = csc.Rootfs[:lastColonIndex] + } else if lastPart == "idmap" || strings.HasPrefix(lastPart, "idmap=") { + csc.RootfsMapping = &lastPart + csc.Rootfs = csc.Rootfs[:lastColonIndex] + } } } else { csc.Image = arg diff --git a/pkg/specgen/specgen_test.go b/pkg/specgen/specgen_test.go index b838d9d30b..b11bd30748 100644 --- a/pkg/specgen/specgen_test.go +++ b/pkg/specgen/specgen_test.go @@ -7,19 +7,31 @@ import ( ) func TestNewSpecGeneratorWithRootfs(t *testing.T) { + idmap := "idmap" + idmapMappings := "idmap=uids=1-1-2000" tests := []struct { rootfs string expectedRootfsOverlay bool expectedRootfs string + expectedMapping *string }{ - {"/root/a:b:O", true, "/root/a:b"}, - {"/root/a:b/c:O", true, "/root/a:b/c"}, - {"/root/a:b/c:", false, "/root/a:b/c:"}, - {"/root/a/b", false, "/root/a/b"}, + {"/root/a:b:O", true, "/root/a:b", nil}, + {"/root/a:b/c:O", true, "/root/a:b/c", nil}, + {"/root/a:b/c:", false, "/root/a:b/c:", nil}, + {"/root/a/b", false, "/root/a/b", nil}, + {"/root/a:b/c:idmap", false, "/root/a:b/c", &idmap}, + {"/root/a:b/c:idmap=uids=1-1-2000", false, "/root/a:b/c", &idmapMappings}, } for _, args := range tests { val := NewSpecGenerator(args.rootfs, true) + assert.Equal(t, val.RootfsOverlay, args.expectedRootfsOverlay) assert.Equal(t, val.Rootfs, args.expectedRootfs) + if args.expectedMapping == nil { + assert.Nil(t, val.RootfsMapping) + } else { + assert.NotNil(t, val.RootfsMapping) + assert.Equal(t, *val.RootfsMapping, *args.expectedMapping) + } } } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index e7952a0bc3..4e40802029 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -502,6 +502,19 @@ func IDtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxI return convertedIDMap } +// RuntimeSpecToIDtoolsTo converts runtime spec to the one of the idtools ID mapping +func RuntimeSpecToIDtools(idMaps []specs.LinuxIDMapping) (convertedIDMap []idtools.IDMap) { + for _, idmap := range idMaps { + tempIDMap := idtools.IDMap{ + ContainerID: int(idmap.ContainerID), + HostID: int(idmap.HostID), + Size: int(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} + func LookupUser(name string) (*user.User, error) { // Assume UID lookup first, if it fails look up by username if u, err := user.LookupId(name); err == nil { diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index 70ecf05a24..df1722a003 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" ) @@ -88,3 +89,30 @@ func TestParseInputTime(t *testing.T) { assert.Equal(t, expected, tm) } + +func TestConvertMappings(t *testing.T) { + start := []specs.LinuxIDMapping{ + { + ContainerID: 1, + HostID: 2, + Size: 3, + }, + { + ContainerID: 4, + HostID: 5, + Size: 6, + }, + } + + converted := RuntimeSpecToIDtools(start) + + convertedBack := IDtoolsToRuntimeSpec(converted) + + assert.Equal(t, len(start), len(convertedBack)) + + for i := range start { + assert.Equal(t, start[i].ContainerID, convertedBack[i].ContainerID) + assert.Equal(t, start[i].HostID, convertedBack[i].HostID) + assert.Equal(t, start[i].Size, convertedBack[i].Size) + } +} diff --git a/test/system/030-run.bats b/test/system/030-run.bats index e6ad64bb0a..91e6a37276 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -1021,4 +1021,38 @@ EOF run_podman run --net=host --cgroupns=host --rm $IMAGE sh -c "grep ' / /sys/fs/cgroup ' /proc/self/mountinfo | tail -n 1 | grep '/sys/fs/cgroup ro'" } +@test "podman run - rootfs with idmapped mounts" { + skip_if_rootless "idmapped mounts work only with root for now" + + skip_if_remote "userns=auto is set on the server" + + egrep -q "^containers:" /etc/subuid || skip "no IDs allocated for user 'containers'" + + # check if the underlying file system supports idmapped mounts + check_dir=$PODMAN_TMPDIR/idmap-check + mkdir $check_dir + run_podman '?' run --rm --uidmap=0:1000:10000 --rootfs $check_dir:idmap true + if [[ "$output" == *"failed to create idmapped mount: invalid argument"* ]]; then + skip "idmapped mounts not supported" + fi + + run_podman image mount $IMAGE + src="$output" + + # we cannot use idmap on top of overlay, so we need a copy + romount=$PODMAN_TMPDIR/rootfs + cp -ar "$src" "$romount" + + run_podman image unmount $IMAGE + + run_podman run --rm --uidmap=0:1000:10000 --rootfs $romount:idmap stat -c %u:%g /bin + is "$output" "0:0" + + run_podman run --uidmap=0:1000:10000 --rm --rootfs "$romount:idmap=uids=0-1001-10000;gids=0-1002-10000" stat -c %u:%g /bin + is "$output" "1:2" + + rm -rf $romount +} + + # vim: filetype=sh diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml index 6f9048564c..40a38d67c6 100644 --- a/vendor/github.com/containers/storage/.cirrus.yml +++ b/vendor/github.com/containers/storage/.cirrus.yml @@ -78,6 +78,8 @@ fedora_testing_task: &fedora_testing TEST_DRIVER: "fuse-overlay" - env: TEST_DRIVER: "fuse-overlay-whiteout" + - env: + TEST_DRIVER: "btrfs" # Separate scripts for separate outputs, makes debugging easier. setup_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' @@ -90,6 +92,7 @@ fedora_testing_task: &fedora_testing journal_log_script: '${_JOURNALCMD} || true' +# aufs was dropped between 20.04 and 22.04, can't test it ubuntu_testing_task: &ubuntu_testing <<: *fedora_testing alias: ubuntu_testing @@ -102,6 +105,12 @@ ubuntu_testing_task: &ubuntu_testing TEST_DRIVER: "vfs" - env: TEST_DRIVER: "overlay" + - env: + TEST_DRIVER: "fuse-overlay" + - env: + TEST_DRIVER: "fuse-overlay-whiteout" + - env: + TEST_DRIVER: "btrfs" lint_task: diff --git a/vendor/github.com/containers/storage/Makefile b/vendor/github.com/containers/storage/Makefile index ea2bb6406e..222715e7b8 100644 --- a/vendor/github.com/containers/storage/Makefile +++ b/vendor/github.com/containers/storage/Makefile @@ -3,25 +3,19 @@ export GOPROXY=https://proxy.golang.org .PHONY: \ all \ - binary \ clean \ - cross \ default \ docs \ - gccgo \ help \ install.tools \ local-binary \ local-cross \ local-gccgo \ + local-test \ local-test-integration \ local-test-unit \ local-validate \ lint \ - test \ - test-integration \ - test-unit \ - validate \ vendor PACKAGE := github.com/containers/storage @@ -40,14 +34,12 @@ ifeq ($(shell $(GO) help mod >/dev/null 2>&1 && echo true), true) MOD_VENDOR=-mod=vendor endif -RUNINVM := vagrant/runinvm.sh - -default all: local-binary docs local-validate local-cross local-gccgo test-unit test-integration ## validate all checks, build and cross-build\nbinaries and docs, run tests in a VM +default all: local-binary docs local-validate local-cross ## validate all checks, build and cross-build\nbinaries and docs clean: ## remove all built files $(RM) -f containers-storage containers-storage.* docs/*.1 docs/*.5 -sources := $(wildcard *.go cmd/containers-storage/*.go drivers/*.go drivers/*/*.go pkg/*/*.go pkg/*/*/*.go) +sources := $(wildcard *.go cmd/containers-storage/*.go drivers/*.go drivers/*/*.go internal/*/*.go pkg/*/*.go pkg/*/*/*.go types/*.go) containers-storage: $(sources) ## build using gc on the host $(GO) build $(MOD_VENDOR) -compiler gc $(BUILDFLAGS) ./cmd/containers-storage @@ -56,10 +48,10 @@ codespell: binary local-binary: containers-storage -local-gccgo: ## build using gccgo on the host +local-gccgo gccgo: ## build using gccgo on the host GCCGO=$(PWD)/hack/gccgo-wrapper.sh $(GO) build $(MOD_VENDOR) -compiler gccgo $(BUILDFLAGS) -o containers-storage.gccgo ./cmd/containers-storage -local-cross: ## cross build the binaries for arm, darwin, and freebsd +local-cross cross: ## cross build the binaries for arm, darwin, and freebsd @for target in linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64 linux/ppc64le linux/s390x linux/mips linux/mipsle linux/mips64 linux/mips64le darwin/amd64 windows/amd64 freebsd/amd64 freebsd/arm64 ; do \ os=`echo $${target} | cut -f1 -d/` ; \ arch=`echo $${target} | cut -f2 -d/` ; \ @@ -68,37 +60,21 @@ local-cross: ## cross build the binaries for arm, darwin, and freebsd env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} $(GO) build $(MOD_VENDOR) -compiler gc -tags "$(NATIVETAGS) $(TAGS)" $(FLAGS) -o containers-storage.$${suffix} ./cmd/containers-storage || exit 1 ; \ done -cross: ## cross build the binaries for arm, darwin, and\nfreebsd using VMs - $(RUNINVM) $(MAKE) local-$@ - docs: install.tools ## build the docs on the host $(MAKE) -C docs docs -gccgo: ## build using gccgo using VMs - $(RUNINVM) $(MAKE) local-$@ - -test: local-binary ## build the binaries and run the tests using VMs - $(RUNINVM) $(MAKE) local-binary local-cross local-test-unit local-test-integration +local-test: local-binary local-test-unit local-test-integration ## build the binaries and run the tests -local-test-unit: local-binary ## run the unit tests on the host (requires\nsuperuser privileges) +local-test-unit test-unit: local-binary ## run the unit tests on the host (requires\nsuperuser privileges) @$(GO) test $(MOD_VENDOR) $(BUILDFLAGS) $(TESTFLAGS) $(shell $(GO) list ./... | grep -v ^$(PACKAGE)/vendor) -test-unit: local-binary ## run the unit tests using VMs - $(RUNINVM) $(MAKE) local-$@ - -local-test-integration: local-binary ## run the integration tests on the host (requires\nsuperuser privileges) +local-test-integration test-integration: local-binary ## run the integration tests on the host (requires\nsuperuser privileges) @cd tests; ./test_runner.bash -test-integration: local-binary ## run the integration tests using VMs - $(RUNINVM) $(MAKE) local-$@ - -local-validate: install.tools ## validate DCO and gofmt on the host +local-validate validate: install.tools ## validate DCO and gofmt on the host @./hack/git-validation.sh @./hack/gofmt.sh -validate: ## validate DCO, gofmt, ./pkg/ isolation, golint,\ngo vet and vendor using VMs - $(RUNINVM) $(MAKE) local-$@ - install.tools: $(MAKE) -C tests/tools diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 53999456ea..a648c84594 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.45.3 +1.45.3-dev diff --git a/vendor/github.com/containers/storage/containers.go b/vendor/github.com/containers/storage/containers.go index 106d1d152e..bdb280e07a 100644 --- a/vendor/github.com/containers/storage/containers.go +++ b/vendor/github.com/containers/storage/containers.go @@ -14,6 +14,7 @@ import ( "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/truncindex" digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" ) type containerLocations uint8 @@ -420,6 +421,7 @@ func (r *containerStore) GarbageCollect() error { } // Otherwise remove datadir + logrus.Debugf("removing %q", filepath.Join(r.dir, id)) moreErr := os.RemoveAll(filepath.Join(r.dir, id)) // Propagate first error if moreErr != nil && err == nil { diff --git a/vendor/github.com/containers/storage/drivers/overlay/check.go b/vendor/github.com/containers/storage/drivers/overlay/check.go index 0a0ad7dd55..4371127424 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/check.go +++ b/vendor/github.com/containers/storage/drivers/overlay/check.go @@ -12,6 +12,7 @@ import ( "syscall" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idmap" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/mount" @@ -243,20 +244,20 @@ func supportsIdmappedLowerLayers(home string) (bool, error) { _ = idtools.MkdirAs(upperDir, 0700, 0, 0) _ = idtools.MkdirAs(workDir, 0700, 0, 0) - idmap := []idtools.IDMap{ + mapping := []idtools.IDMap{ { ContainerID: 0, HostID: 0, Size: 1, }, } - pid, cleanupFunc, err := createUsernsProcess(idmap, idmap) + pid, cleanupFunc, err := idmap.CreateUsernsProcess(mapping, mapping) if err != nil { return false, err } defer cleanupFunc() - if err := createIDMappedMount(lowerDir, lowerMappedDir, int(pid)); err != nil { + if err := idmap.CreateIDMappedMount(lowerDir, lowerMappedDir, int(pid)); err != nil { return false, fmt.Errorf("create mapped mount: %w", err) } defer unix.Unmount(lowerMappedDir, unix.MNT_DETACH) diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index e33bf16db2..be7406b9ab 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -26,6 +26,7 @@ import ( "github.com/containers/storage/pkg/chrootarchive" "github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/fsutils" + "github.com/containers/storage/pkg/idmap" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/parsers" @@ -1511,7 +1512,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return "", err } - pid, cleanupFunc, err := createUsernsProcess(options.UidMaps, options.GidMaps) + pid, cleanupFunc, err := idmap.CreateUsernsProcess(options.UidMaps, options.GidMaps) if err != nil { return "", err } @@ -1528,7 +1529,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO if !found { root = filepath.Join(mappedRoot, fmt.Sprintf("%d", c)) c++ - if err := createIDMappedMount(mappedMountSrc, root, int(pid)); err != nil { + if err := idmap.CreateIDMappedMount(mappedMountSrc, root, int(pid)); err != nil { return "", fmt.Errorf("create mapped mount for %q on %q: %w", mappedMountSrc, root, err) } idMappedMounts[mappedMountSrc] = root diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go index 577b6f8ed4..2079c0ff1c 100644 --- a/vendor/github.com/containers/storage/images.go +++ b/vendor/github.com/containers/storage/images.go @@ -14,6 +14,7 @@ import ( "github.com/containers/storage/pkg/stringutils" "github.com/containers/storage/pkg/truncindex" digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" ) const ( @@ -152,6 +153,9 @@ type rwImageStore interface { addMappedTopLayer(id, layer string) error removeMappedTopLayer(id, layer string) error + // Clean up unreferenced per-image data. + GarbageCollect() error + // Wipe removes records of all images. Wipe() error } @@ -396,6 +400,41 @@ func (r *imageStore) Images() ([]Image, error) { return images, nil } +// This looks for datadirs in the store directory that are not referenced +// by the json file and removes it. These can happen in the case of unclean +// shutdowns. +// Requires startReading or startWriting. +func (r *imageStore) GarbageCollect() error { + entries, err := os.ReadDir(r.dir) + if err != nil { + // Unexpected, don't try any GC + return err + } + + for _, entry := range entries { + id := entry.Name() + // Does it look like a datadir directory? + if !entry.IsDir() || !nameLooksLikeID(id) { + continue + } + + // Should the id be there? + if r.byid[id] != nil { + continue + } + + // Otherwise remove datadir + logrus.Debugf("removing %q", filepath.Join(r.dir, id)) + moreErr := os.RemoveAll(filepath.Join(r.dir, id)) + // Propagate first error + if moreErr != nil && err == nil { + err = moreErr + } + } + + return err +} + func (r *imageStore) imagespath() string { return filepath.Join(r.dir, "images.json") } diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index f14108be5a..2f06ce8090 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -678,10 +678,13 @@ func (r *layerStore) GarbageCollect() error { // Remove layer and any related data of unreferenced id if err := r.driver.Remove(id); err != nil { + logrus.Debugf("removing driver layer %q", id) return err } + logrus.Debugf("removing %q", r.tspath(id)) os.Remove(r.tspath(id)) + logrus.Debugf("removing %q", r.datadir(id)) os.RemoveAll(r.datadir(id)) } return nil diff --git a/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go b/vendor/github.com/containers/storage/pkg/idmap/idmapped_utils.go similarity index 93% rename from vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go rename to vendor/github.com/containers/storage/pkg/idmap/idmapped_utils.go index 0b7c868ac6..98ce707271 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/idmapped_utils.go +++ b/vendor/github.com/containers/storage/pkg/idmap/idmapped_utils.go @@ -1,7 +1,7 @@ //go:build linux // +build linux -package overlay +package idmap import ( "fmt" @@ -77,9 +77,9 @@ func mountSetAttr(dfd int, path string, flags uint, attr *attr, size uint) (err return } -// createIDMappedMount creates a IDMapped bind mount from SOURCE to TARGET using the user namespace +// CreateIDMappedMount creates a IDMapped bind mount from SOURCE to TARGET using the user namespace // for the PID process. -func createIDMappedMount(source, target string, pid int) error { +func CreateIDMappedMount(source, target string, pid int) error { path := fmt.Sprintf("/proc/%d/ns/user", pid) userNsFile, err := os.Open(path) if err != nil { @@ -110,9 +110,9 @@ func createIDMappedMount(source, target string, pid int) error { return moveMount(targetDirFd, target) } -// createUsernsProcess forks the current process and creates a user namespace using the specified +// CreateUsernsProcess forks the current process and creates a user namespace using the specified // mappings. It returns the pid of the new process. -func createUsernsProcess(uidMaps []idtools.IDMap, gidMaps []idtools.IDMap) (int, func(), error) { +func CreateUsernsProcess(uidMaps []idtools.IDMap, gidMaps []idtools.IDMap) (int, func(), error) { var pid uintptr var err syscall.Errno diff --git a/vendor/github.com/containers/storage/pkg/idmap/idmapped_utils_unsupported.go b/vendor/github.com/containers/storage/pkg/idmap/idmapped_utils_unsupported.go new file mode 100644 index 0000000000..81c6072aad --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idmap/idmapped_utils_unsupported.go @@ -0,0 +1,22 @@ +//go:build !linux +// +build !linux + +package idmap + +import ( + "fmt" + + "github.com/containers/storage/pkg/idtools" +) + +// CreateIDMappedMount creates a IDMapped bind mount from SOURCE to TARGET using the user namespace +// for the PID process. +func CreateIDMappedMount(source, target string, pid int) error { + return fmt.Errorf("IDMapped mounts are not supported") +} + +// CreateUsernsProcess forks the current process and creates a user namespace using the specified +// mappings. It returns the pid of the new process. +func CreateUsernsProcess(uidMaps []idtools.IDMap, gidMaps []idtools.IDMap) (int, func(), error) { + return -1, nil, fmt.Errorf("IDMapped mounts are not supported") +} diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index d208e0bfaf..c788c3f145 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -3341,7 +3341,14 @@ func (s *store) GarbageCollect() error { return s.containerStore.GarbageCollect() }) - moreErr := s.writeToLayerStore(func(rlstore rwLayerStore) error { + moreErr := s.writeToImageStore(func() error { + return s.imageStore.GarbageCollect() + }) + if firstErr == nil { + firstErr = moreErr + } + + moreErr = s.writeToLayerStore(func(rlstore rwLayerStore) error { return rlstore.GarbageCollect() }) if firstErr == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index f9d6e26e43..946e7c15f7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -79,8 +79,8 @@ github.com/containerd/containerd/log github.com/containerd/containerd/pkg/userns github.com/containerd/containerd/platforms github.com/containerd/containerd/sys -# github.com/containerd/stargz-snapshotter/estargz v0.13.0 -## explicit; go 1.16 +# github.com/containerd/stargz-snapshotter/estargz v0.14.1 +## explicit; go 1.19 github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz/errorutil # github.com/containernetworking/cni v1.1.2 @@ -276,7 +276,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.45.3 +# github.com/containers/storage v1.45.3 => github.com/containers/storage v1.45.3-0.20230131031022-998b8bf212eb ## explicit; go 1.17 github.com/containers/storage github.com/containers/storage/drivers @@ -303,6 +303,7 @@ github.com/containers/storage/pkg/dmesg github.com/containers/storage/pkg/fileutils github.com/containers/storage/pkg/fsutils github.com/containers/storage/pkg/homedir +github.com/containers/storage/pkg/idmap github.com/containers/storage/pkg/idtools github.com/containers/storage/pkg/ioutils github.com/containers/storage/pkg/locker @@ -1130,3 +1131,4 @@ gopkg.in/yaml.v3 ## explicit; go 1.12 sigs.k8s.io/yaml # github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.1-0.20220617142545-8b9452f75cbc +# github.com/containers/storage => github.com/containers/storage v1.45.3-0.20230131031022-998b8bf212eb