Repository: containers/podman
Pull request #17274 (2023-01-30 09:28:21)

libpod: support idmap for --rootfs
#### Does this PR introduce a user-facing change?

allows to specify the option ":idmap" to the --rootfs argument

```release-note
Now --rootfs supports the ":idmap" option to create an idmapped mount on the rootfs directory.
```


openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/containers/podman/pull/17274#" title="Author self-approved">giuseppe</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=containers%2Fpodman).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/containers/podman/blob/main/OWNERS)~~ [giuseppe]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
giuseppe: to get unblocked I'll need https://github.com/containers/storage/pull/1489

Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index ee21557f27..5c2a7fa822 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -25,14 +25,15 @@ env:
 
     ####
     #### Cache-image names to test with (double-quotes around names are critical)
+    #### Comment out fedora-35 for podman 4.x branches.
     ####
-    FEDORA_NAME: "fedora-37" ### c20230120t152650z-f37f36u2204
-    FEDORA_AARCH64_NAME: "${FEDORA_NAME}-aarch64" ### c20230120t152650z-f37f36u2204
-    PRIOR_FEDORA_NAME: "fedora-36" ### c20230120t152650z-f37f36u2204
-    #UBUNTU_NAME: "ubuntu-2204"  ### c20230120t152650z-f37f36u2204
+    FEDORA_NAME: "fedora-37"
+    FEDORA_AARCH64_NAME: "${FEDORA_NAME}-aarch64"
+    PRIOR_FEDORA_NAME: "fedora-36"
+    UBUNTU_NAME: "ubuntu-2204"
 
     # Image identifiers
-    IMAGE_SUFFIX: "c20230120t152650z-f37f36u2204"
+    IMAGE_SUFFIX: "c5841131699699712"
     # EC2 images
     FEDORA_AMI: "fedora-aws-${IMAGE_SUFFIX}"
     FEDORA_AARCH64_AMI: "fedora-podman-aws-arm64-${IMAGE_SUFFIX}"
@@ -854,6 +855,34 @@ buildah_bud_test_task:
     always: *int_logs_artifacts
 
 
+#rootless_gitlab_test_task:
+#    name: *std_name_fmt
+#    alias: rootless_gitlab_test
+#    # Docs: ./contrib/cirrus/CIModes.md
+#    only_if: &cirrus_cron "${CIRRUS_CRON} == 'main'"
+#    # Community-maintained downstream test may fail unexpectedly.
+#    # Ref. repository: https://gitlab.com/gitlab-org/gitlab-runner
+#    # If necessary, uncomment the next line and file issue(s) with details.
+#    # allow_failures: $CI == $CI
+#    depends_on:
+#        - build
+#        - rootless_integration_test
+#    gce_instance: *standardvm
+#    env:
+#        <<: *ubuntu_envvars
+#        TEST_FLAVOR: 'gitlab'
+#        PRIV_NAME: rootless
+#    clone_script: *get_gosrc
+#    setup_script: *setup
+#    main_script: *main
+#    always:
+#        <<: *logs_artifacts
+#        junit_artifacts:
+#            path: gitlab-runner-podman.xml
+#            type: text/xml
+#            format: junit
+
+
 upgrade_test_task:
     name: "Upgrade test: from $PODMAN_UPGRADE_FROM"
     alias: upgrade_test
@@ -999,6 +1028,7 @@ success_task:
         - rootless_remote_system_test
         - minikube_test
         - buildah_bud_test
+        #- rootless_gitlab_test
         - upgrade_test
         - image_build
         - meta
diff --git a/contrib/cirrus/prebuild.sh b/contrib/cirrus/prebuild.sh
index 583f26bf5f..d4306a17ac 100755
--- a/contrib/cirrus/prebuild.sh
+++ b/contrib/cirrus/prebuild.sh
@@ -16,7 +16,7 @@ set +a
 
 req_env_vars CI DEST_BRANCH IMAGE_SUFFIX TEST_FLAVOR TEST_ENVIRON \
              PODBIN_NAME PRIV_NAME DISTRO_NV AUTOMATION_LIB_PATH \
-             SCRIPT_BASE CIRRUS_WORKING_DIR FEDORA_NAME \
+             SCRIPT_BASE CIRRUS_WORKING_DIR FEDORA_NAME UBUNTU_NAME \
              VM_IMAGE_NAME
 
 # Defined by the CI system
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/hack/perf/bz-2162111.sh b/hack/perf/bz-2162111.sh
index 7bee29a48d..75b6632768 100755
--- a/hack/perf/bz-2162111.sh
+++ b/hack/perf/bz-2162111.sh
@@ -21,33 +21,23 @@ container_cmd="--name $container_name \
 	-v $file_b:/home/file_b:rw \
 	--cap-drop=ALL \
 	--ulimit nofile=1024:2048 \
-	$IMAGE"
+	$IMAGE true"
 
 # Script to clean up before each benchmark below
 prepare_sh=$(mktemp -p $tmp --suffix '.prepare.sh')
 cat >$prepare_sh <<EOF
-\$ENGINE rm -f \$(\$ENGINE ps -aq)
-\$ENGINE volume prune -f
-\$ENGINE volume create $volume_name
+\$ENGINE rm -f \$(\$ENGINE ps -aq)  > /dev/null
+\$ENGINE volume prune -f            > /dev/null
+\$ENGINE volume create $volume_name > /dev/null
 EOF
 echo_bold "Prepare script: $prepare_sh"
 
 # Script to create container below
 create_sh=$(mktemp -p $tmp --suffix '.create.sh')
 cat >$create_sh <<EOF
-\$ENGINE create $container_cmd true
+\$ENGINE create $container_cmd > /dev/null
 EOF
-echo_bold "Create script: $create_sh"
-
-# Script to run container below
-run_sh=$(mktemp -p $tmp --suffix '.run.sh')
-cat >$run_sh <<EOF
-# Make sure to remove the container from a previous run
-\$ENGINE rm -f $container_name || true
-# Run the container
-\$ENGINE run -d $container_cmd top > /dev/null
-EOF
-echo_bold "Run script: $run_sh"
+echo_bold "Create script: $prepare_sh"
 
 echo ""
 echo "----------------------------------------------------"
@@ -56,8 +46,8 @@ echo_bold "Create $NUM_CONTAINERS containers"
 hyperfine --warmup 10 --runs $RUNS \
 	--prepare "ENGINE=$ENGINE_A sh $prepare_sh" \
 	--prepare "ENGINE=$ENGINE_B sh $prepare_sh" \
-	"$ENGINE_A create $container_cmd true" \
-	"$ENGINE_B create $container_cmd true"
+	"$ENGINE_A create $container_cmd" \
+	"$ENGINE_B create $container_cmd"
 
 echo ""
 echo "----------------------------------------------------"
@@ -69,16 +59,6 @@ hyperfine --warmup 10 --runs $RUNS \
 	"$ENGINE_A start $container_name" \
 	"$ENGINE_B start $container_name"
 
-echo ""
-echo "----------------------------------------------------"
-echo
-echo_bold "Stop $NUM_CONTAINERS containers"
-hyperfine --warmup 10 --runs $RUNS \
-	--prepare "ENGINE=$ENGINE_A sh $run_sh" \
-	--prepare "ENGINE=$ENGINE_B sh $run_sh" \
-	"$ENGINE_A stop $container_name" \
-	"$ENGINE_B stop $container_name"
-
 echo ""
 echo "----------------------------------------------------"
 echo
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/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/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/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,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


Test patch:
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/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_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/test/system/160-volumes.bats b/test/system/160-volumes.bats
index 7c8e4610de..4bdbb41cee 100644
--- a/test/system/160-volumes.bats
+++ b/test/system/160-volumes.bats
@@ -178,8 +178,13 @@ EOF
 
     # By default, volumes are mounted exec, but we have manually added the
     # noexec option. This should fail.
-    run_podman 126 run --rm --volume $myvolume:/vol:noexec,z $IMAGE /vol/myscript
+    # ARGH. Unfortunately, runc (used for cgroups v1) has different exit status
+    local expect_rc=126
+    if [[ $(podman_runtime) = "runc" ]]; then
+        expect_rc=1
+    fi
 
+    run_podman ${expect_rc} run --rm --volume $myvolume:/vol:noexec,z $IMAGE /vol/myscript
     # crun and runc emit different messages, and even runc is inconsistent
     # with itself (output changed some time in 2022?). Deal with all.
     assert "$output" =~ 'exec.* permission denied' "run on volume, noexec"
diff --git a/test/system/252-quadlet.bats b/test/system/252-quadlet.bats
index 8feed02c50..4e862c6108 100644
--- a/test/system/252-quadlet.bats
+++ b/test/system/252-quadlet.bats
@@ -78,20 +78,14 @@ function service_setup() {
         local activestate="inactive"
     fi
 
-    echo "$_LOG_PROMPT systemctl $startargs start $service"
     run systemctl $startargs start "$service"
-    echo "$output"
-    assert $status -eq 0 "Error starting systemd unit $service"
+    assert $status -eq 0 "Error starting systemd unit $service: $output"
 
-    echo "$_LOG_PROMPT systemctl status $service"
     run systemctl status "$service"
-    echo "$output"
-    assert $status -eq $statusexit "systemctl status $service"
+    assert $status -eq $statusexit "systemctl status $service: $output"
 
-    echo "$_LOG_PROMPT systemctl show --value --property=ActiveState $service"
-    run systemctl show --value --property=ActiveState "$service"
-    echo "$output"
-    assert $status -eq 0 "systemctl show $service"
+    run systemctl show -P ActiveState "$service"
+    assert $status -eq 0 "systemctl show $service: $output"
     is "$output" $activestate
 }