From 10627228be20d317752ebfaf8b8b54469a4793ea Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Thu, 30 Jul 2020 22:59:45 +0200
Subject: [PATCH] volumes: do not recurse when chowning

keep the file ownership when chowning and honor the user namespace
mappings.

Closes: https://github.com/containers/podman/issues/7130

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
 libpod/container_internal.go | 35 ++++++++++++++++++++++-------------
 test/system/070-build.bats   |  8 ++++----
 2 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index a5208a0df3..f3f11f9459 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1534,9 +1534,6 @@ func (c *Container) chownVolume(volumeName string) error {
 		return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID())
 	}
 
-	uid := int(c.config.Spec.Process.User.UID)
-	gid := int(c.config.Spec.Process.User.GID)
-
 	vol.lock.Lock()
 	defer vol.lock.Unlock()
 
@@ -1547,22 +1544,34 @@ func (c *Container) chownVolume(volumeName string) error {
 
 	if vol.state.NeedsChown {
 		vol.state.NeedsChown = false
+
+		uid := int(c.config.Spec.Process.User.UID)
+		gid := int(c.config.Spec.Process.User.GID)
+
+		if c.config.IDMappings.UIDMap != nil {
+			p := idtools.IDPair{
+				UID: uid,
+				GID: gid,
+			}
+			mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap)
+			newPair, err := mappings.ToHost(p)
+			if err != nil {
+				return errors.Wrapf(err, "error mapping user %d:%d", uid, gid)
+			}
+			uid = newPair.UID
+			gid = newPair.GID
+		}
+
 		vol.state.UIDChowned = uid
 		vol.state.GIDChowned = gid
 
 		if err := vol.save(); err != nil {
 			return err
 		}
-		err := filepath.Walk(vol.MountPoint(), func(path string, info os.FileInfo, err error) error {
-			if err != nil {
-				return err
-			}
-			if err := os.Lchown(path, uid, gid); err != nil {
-				return err
-			}
-			return nil
-		})
-		if err != nil {
+
+		mountPoint := vol.MountPoint()
+
+		if err := os.Lchown(mountPoint, uid, gid); err != nil {
 			return err
 		}
 	}
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 627b9caa6b..a69d32a2f8 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -143,7 +143,7 @@ RUN mkdir -p /a/b/c
 RUN ln -s /no/such/nonesuch /a/b/c/badsymlink
 RUN ln -s /bin/mydefaultcmd /a/b/c/goodsymlink
 RUN touch /a/b/c/myfile
-RUN chown -h 1:2 /a/b/c/badsymlink /a/b/c/goodsymlink /a/b/c/myfile
+RUN chown -h 1:2 /a/b/c/badsymlink /a/b/c/goodsymlink && chown -h 4:5 /a/b/c/myfile
 VOLUME /a/b/c
 
 # Test for environment passing and override
@@ -216,18 +216,18 @@ Labels.$label_name | $label_value
     # be they dangling or valid, would barf with
     #    Error: chown <mountpath>/_data/symlink: ENOENT
     run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/badsymlink
-    is "$output" "0:0:'/a/b/c/badsymlink' -> '/no/such/nonesuch'" \
+    is "$output" "1:2:'/a/b/c/badsymlink' -> '/no/such/nonesuch'" \
        "bad symlink to nonexistent file is chowned and preserved"
 
     run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/goodsymlink
-    is "$output" "0:0:'/a/b/c/goodsymlink' -> '/bin/mydefaultcmd'" \
+    is "$output" "1:2:'/a/b/c/goodsymlink' -> '/bin/mydefaultcmd'" \
        "good symlink to existing file is chowned and preserved"
 
     run_podman run --rm build_test stat -c'%u:%g' /bin/mydefaultcmd
     is "$output" "2:3" "target of symlink is not chowned"
 
     run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/myfile
-    is "$output" "0:0:/a/b/c/myfile" "file in volume is chowned to root"
+    is "$output" "4:5:/a/b/c/myfile" "file in volume is chowned"
 
     # Clean up
     run_podman rmi -f build_test