diff --git a/usr/lib/python3.8/spoc/imagebuilder.py b/usr/lib/python3.8/spoc/imagebuilder.py index 1ec4197..37b9322 100644 --- a/usr/lib/python3.8/spoc/imagebuilder.py +++ b/usr/lib/python3.8/spoc/imagebuilder.py @@ -112,10 +112,10 @@ class ImageBuilder: unpack_http_archive(src, dst) else: src = os.path.join(self.builddir, src) - if not os.path.isdir(src): - shutil.copy2(src, dst) + if os.path.isdir(src): + copy_tree(src, dst) else: - shutil.copytree(src, dst, symlinks=True, ignore_dangling_symlinks=True, dirs_exist_ok=True) + shutil.copy2(src, dst) # Shift UID/GID of the files to the unprivileged range shift_uid(dst, os.stat(dst, follow_symlinks=False)) @@ -141,6 +141,22 @@ def unpack_http_archive(src, dst): with tarfile.open(fileobj=tmp_archive) as tar: tar.extractall(dst, numeric_owner=True) +def copy_tree(src, dst): + # Copy directory tree from host to container, leaving the existing modes and attributed unchanged, + # which is crucial e.g. whenever anything is copied into /tmp + # This function is a stripped and customized variant of shutil.copytree() + for srcentry in os.scandir(src): + dstname = os.path.join(dst, srcentry.name) + is_new = not os.path.exists(dstname) + if srcentry.is_dir(): + if is_new: + os.mkdir(dstname) + copy_tree(srcentry, dstname) + else: + shutil.copy2(srcentry, dstname) + if is_new: + shutil.copystat(srcentry, dstname, follow_symlinks=False) + def shift_uid(path, path_stat): # Shifts UID/GID of a file or a directory and its contents to the unprivileged range # The function parameters could arguably be more friendly, but os.scandir() already calls stat() on the entires,