Fix copyUIDGID parameter inversion in Docker compat API

Docker API's copyUIDGID=true means "preserve UID/GID from archive"
but Podman's internal Chown=true means "chown to container user".
This caused Docker SDK clients to have files incorrectly chowned
to root:root instead of preserving the archive's UID/GID.

Fixes: https://github.com/containers/podman/issues/27332
Fixes: https://issues.redhat.com/browse/RUN-3643

Signed-off-by: Jan Rodák <hony.com@seznam.cz>
This commit is contained in:
Jan Rodák
2025-10-27 11:49:31 +01:00
parent 5a0b74b13e
commit 2b848cca36
3 changed files with 51 additions and 4 deletions

View File

@@ -124,9 +124,17 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder,
containerName := utils.GetName(r)
containerEngine := abi.ContainerEngine{Libpod: runtime}
// Docker API semantics: copyUIDGID=true means "preserve UID/GID from archive"
// Podman internal semantics: Chown=true means "chown to container user" (override archive)
// For compat requests, we need to invert the value
chown := query.Chown
if !utils.IsLibpodRequest(r) {
chown = !query.Chown
}
copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body,
entities.CopyOptions{
Chown: query.Chown,
Chown: chown,
NoOverwriteDirNonDir: query.NoOverwriteDirNonDir,
Rename: rename,
})

View File

@@ -40,7 +40,7 @@ t HEAD "containers/${CTR}/archive?path=%2Fnon%2Fexistent%2Fpath" 404
t HEAD "containers/${CTR}/archive?path=%2Fetc%2Fpasswd" 200
# Send tarfile to container...
t PUT "/containers/${CTR}/archive?path=%2Ftmp%2F" ${HELLO_TAR} 200 ''
t PUT "/containers/${CTR}/archive?path=%2Ftmp%2F&copyUIDGID=true" ${HELLO_TAR} 200 ''
# ...and 'exec cat file' to confirm that it got extracted into place.
cat >$TMPD/exec.json <<EOF
@@ -80,6 +80,44 @@ EOF
t POST containers/${CTR}/exec $TMPD/exec.json 201 .Id~[0-9a-f]\\{64\\}
eid=$(jq -r '.Id' <<<"$output")
t POST exec/$eid/start 200 $'\001\012'1042:1043
t POST exec/$eid/start 200
output_uidgid=$(grep -o '[0-9]*:[0-9]*' <<<"$output")
is "$output_uidgid" "1042:1043" "UID:GID preserved with copyUIDGID=true"
FILE_NAME=test1
TAR_PATH="${TMPD}/${FILE_NAME}.tar"
echo "Hello2_$(random_string 8)" > ${TMPD}/${FILE_NAME}.txt
tar --owner=2001 --group=2002 --format=posix -C $TMPD -cvf ${TAR_PATH} ${FILE_NAME}.txt &> /dev/null
t PUT "/containers/${CTR}/archive?path=%2Ftmp%2F" ${TAR_PATH} 200 ''
cat >$TMPD/exec.json <<EOF
{ "AttachStdout":true,"Cmd":["stat","-c","%u:%g","/tmp/${FILE_NAME}.txt"]}
EOF
t POST containers/${CTR}/exec $TMPD/exec.json 201 .Id~[0-9a-f]\\{64\\}
eid=$(jq -r '.Id' <<<"$output")
t POST exec/$eid/start 200
output_uidgid=$(grep -o '[0-9]*:[0-9]*' <<<"$output")
is "$output_uidgid" "0:0" "UID:GID chowned to container user without copyUIDGID"
# --- libpod
FILE_NAME=test3
TAR_PATH="${TMPD}/${FILE_NAME}.tar"
echo "test3_$(random_string 8)" > ${TMPD}/${FILE_NAME}.txt
tar --owner=4001 --group=4002 --format=posix -C $TMPD -cvf ${TAR_PATH} ${FILE_NAME}.txt &> /dev/null
t PUT "libpod/containers/${CTR}/archive?path=%2Ftmp%2F" ${TAR_PATH} 200 ''
cat >$TMPD/exec.json <<EOF
{ "AttachStdout":true,"Cmd":["stat","-c","%u:%g","/tmp/${FILE_NAME}.txt"]}
EOF
t POST containers/${CTR}/exec $TMPD/exec.json 201 .Id~[0-9a-f]\\{64\\}
eid=$(jq -r '.Id' <<<"$output")
t POST exec/$eid/start 200
output_uidgid=$(grep -o '[0-9]*:[0-9]*' <<<"$output")
is "$output_uidgid" "0:0" "libpod API: UID:GID chowned to container user"
cleanUpArchiveTest

View File

@@ -191,7 +191,8 @@ class TestContainers(common.DockerTestCase):
ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/tmp/a.txt"])
self.assertEqual(ret, 0)
self.assertEqual(out.rstrip(), b"1042:1043", "UID/GID of copied file")
# Docker-py implementation of put_archive dont do request with copyUIDGID=true
self.assertEqual(out.rstrip(), b"0:0", "UID/GID of copied file")
ret, out = ctr.exec_run(["cat", "/tmp/a.txt"])
self.assertEqual(ret, 0)