Skip to content

Commit f9261b7

Browse files
mayeutjoerick
authored andcommitted
fix: file ownership of files copied into the container
Revert to using `cat`/`tar` to copy files/folders into the container.
1 parent 33da1f7 commit f9261b7

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

cibuildwheel/oci_container.py

+35-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import platform
77
import shlex
8+
import shutil
89
import subprocess
910
import sys
1011
import typing
@@ -169,6 +170,9 @@ def __init__(
169170
self.cwd = cwd
170171
self.name: str | None = None
171172
self.engine = engine
173+
self.host_tar_format = ""
174+
if sys.platform.startswith("darwin"):
175+
self.host_tar_format = "--format gnutar"
172176

173177
def _get_platform_args(self, *, oci_platform: OCIPlatform | None = None) -> tuple[str, str]:
174178
if oci_platform is None:
@@ -311,10 +315,38 @@ def __exit__(
311315
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
312316
if from_path.is_dir():
313317
self.call(["mkdir", "-p", to_path])
314-
call(self.engine.name, "cp", f"{from_path}/.", f"{self.name}:{to_path}")
318+
subprocess.run(
319+
f"tar -c {self.host_tar_format} -f - . | {self.engine.name} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -",
320+
shell=True,
321+
check=True,
322+
cwd=from_path,
323+
)
315324
else:
316-
self.call(["mkdir", "-p", to_path.parent])
317-
call(self.engine.name, "cp", from_path, f"{self.name}:{to_path}")
325+
exec_process: subprocess.Popen[bytes]
326+
with subprocess.Popen(
327+
[
328+
self.engine.name,
329+
"exec",
330+
"-i",
331+
str(self.name),
332+
"sh",
333+
"-c",
334+
f"cat > {shell_quote(to_path)}",
335+
],
336+
stdin=subprocess.PIPE,
337+
) as exec_process:
338+
assert exec_process.stdin
339+
with open(from_path, "rb") as from_file:
340+
# Bug in mypy, https://github.com/python/mypy/issues/15031
341+
shutil.copyfileobj(from_file, exec_process.stdin) # type: ignore[misc]
342+
343+
exec_process.stdin.close()
344+
exec_process.wait()
345+
346+
if exec_process.returncode:
347+
raise subprocess.CalledProcessError(
348+
exec_process.returncode, exec_process.args, None, None
349+
)
318350

319351
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
320352
# note: we assume from_path is a dir

unit_test/oci_container_test.py

+14
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ def test_file_operation(tmp_path: Path, container_engine):
244244

245245
container.copy_into(original_test_file, dst_file)
246246

247+
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
248+
assert owner == "0:0"
249+
247250
output = container.call(["cat", dst_file], capture_output=True)
248251
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
249252

@@ -266,6 +269,12 @@ def test_dir_operations(tmp_path: Path, container_engine):
266269
dst_file = dst_dir / "test.dat"
267270
container.copy_into(test_dir, dst_dir)
268271

272+
owner = container.call(["stat", "-c", "%u:%g", dst_dir], capture_output=True).strip()
273+
assert owner == "0:0"
274+
275+
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
276+
assert owner == "0:0"
277+
269278
output = container.call(["cat", dst_file], capture_output=True)
270279
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
271280

@@ -276,6 +285,11 @@ def test_dir_operations(tmp_path: Path, container_engine):
276285
new_test_dir = tmp_path / "test_dir_new"
277286
container.copy_out(dst_dir, new_test_dir)
278287

288+
assert os.getuid() == new_test_dir.stat().st_uid
289+
assert os.getgid() == new_test_dir.stat().st_gid
290+
assert os.getuid() == (new_test_dir / "test.dat").stat().st_uid
291+
assert os.getgid() == (new_test_dir / "test.dat").stat().st_gid
292+
279293
assert test_binary_data == (new_test_dir / "test.dat").read_bytes()
280294

281295

0 commit comments

Comments
 (0)