from mkosi.util import (
PathString,
chdir,
+ copyfile,
+ copyfile2,
flatten,
flock_or_die,
format_rlimit,
newosrelease.rename(osrelease)
if ArtifactOutput.os_release in context.config.split_artifacts:
- shutil.copy(osrelease, context.staging / context.config.output_split_os_release)
+ copyfile(osrelease, context.staging / context.config.output_split_os_release)
def configure_extension_release(context: Context) -> None:
dest = veritydir / context.config.verity_certificate.with_suffix(".crt").name
with umask(~0o644):
- shutil.copy(context.config.verity_certificate, dest)
+ copyfile(context.config.verity_certificate, dest)
def configure_mountpoints(context: Context) -> None:
install_tree(config, tree.source, dst, target=tree.target, preserve=False)
if Path("/etc/passwd").exists():
- shutil.copy("/etc/passwd", dst / "etc/passwd")
+ copyfile("/etc/passwd", dst / "etc/passwd")
if Path("/etc/group").exists():
- shutil.copy("/etc/group", dst / "etc/group")
+ copyfile("/etc/group", dst / "etc/group")
if not (dst / "etc/mtab").is_symlink():
(dst / "etc/mtab").symlink_to("../proc/self/mounts")
)
if not (dst / "etc/hosts").exists() and Path("/etc/hosts").exists():
- shutil.copy("/etc/hosts", dst / "etc/hosts")
+ copyfile("/etc/hosts", dst / "etc/hosts")
Path(dst / "etc/static").unlink(missing_ok=True)
if (config.tools() / "etc/static").is_symlink():
context.config
).package_globs()
):
- shutil.copy(p, context.repository, follow_symlinks=True)
+ copyfile(p, context.repository)
def install_extra_trees(context: Context) -> None:
if vmlinuz.is_symlink() and vmlinuz.resolve().is_relative_to("/boot"):
vmlinuz.unlink()
if not vmlinuz.exists():
- shutil.copy2(d, vmlinuz)
+ copyfile2(d, vmlinuz)
def want_initrd(context: Context) -> bool:
maybe_compress(context, compression, kmods, kmods)
if ArtifactOutput.kernel_modules_initrd in context.config.split_artifacts:
- shutil.copy(kmods, context.staging / context.config.output_split_kernel_modules_initrd)
+ copyfile(kmods, context.staging / context.config.output_split_kernel_modules_initrd)
return kmods
):
kimg = sign_efi_binary(context, kimg, dst / "vmlinuz")
else:
- kimg = Path(shutil.copy2(context.root / kimg, dst / "vmlinuz"))
+ kimg = Path(copyfile2(context.root / kimg, dst / "vmlinuz"))
- initrds = [
- Path(shutil.copy2(initrd, dst.parent / initrd.name)) for initrd in microcode + initrds
- ] + [Path(shutil.copy2(kmods, dst / "kernel-modules.initrd"))]
+ initrds = [Path(copyfile2(initrd, dst.parent / initrd.name)) for initrd in microcode + initrds] + [
+ Path(copyfile2(kmods, dst / "kernel-modules.initrd"))
+ ]
if dtb and source_dtb:
- shutil.copy2(source_dtb, dtb)
+ copyfile2(source_dtb, dtb)
with entry.open("w") as f:
f.write(
) or context.config.unified_kernel_images == UnifiedKernelImage.signed:
for p in (context.root / "usr/lib/modules" / kver).glob("*.efi"):
log_step(f"Installing prebuilt UKI at {p} to {boot_binary}")
- shutil.copy2(p, boot_binary)
+ copyfile2(p, boot_binary)
break
else:
if context.config.bootable == ConfigFeature.enabled:
return None
with complete_step("Copying nspawn settings fileā¦"):
- shutil.copy2(context.config.nspawn_settings, context.staging / context.config.output_nspawn_settings)
+ copyfile2(context.config.nspawn_settings, context.staging / context.config.output_nspawn_settings)
def get_uki_path(context: Context) -> Optional[Path]:
return
if uki := get_uki_path(context):
- shutil.copy(uki, context.staging / context.config.output_split_uki)
+ copyfile(uki, context.staging / context.config.output_split_uki)
def copy_vmlinuz(context: Context) -> None:
return
for _, kimg in gen_kernel_images(context):
- shutil.copy(context.root / kimg, context.staging / context.config.output_split_kernel)
+ copyfile(context.root / kimg, context.staging / context.config.output_split_kernel)
break
if not stub.exists():
die(f"sd-stub not found at /{stub.relative_to(context.root)} in the image")
- return Path(shutil.copy2(stub, context.workspace)), None, None, []
+ return Path(copyfile2(stub, context.workspace)), None, None, []
if context.config.output_format not in (OutputFormat.uki, OutputFormat.esp):
return None, None, None, []
return None, None, None, []
- kimg = Path(shutil.copy2(context.root / kimg, context.workspace))
+ kimg = Path(copyfile2(context.root / kimg, context.workspace))
if not context.config.architecture.to_efi():
die(f"Architecture {context.config.architecture} does not support UEFI")
if not stub.exists():
die(f"sd-stub not found at /{stub.relative_to(context.root)} in the image")
- stub = Path(shutil.copy2(stub, context.workspace))
+ stub = Path(copyfile2(stub, context.workspace))
microcode = build_microcode_initrd(context)
return stub, kver, kimg, microcode
stack.callback(
lambda: (config.output_dir_or_cwd() / f"{name}.nspawn").unlink(missing_ok=True)
)
- shutil.copy2(config.nspawn_settings, config.output_dir_or_cwd() / f"{name}.nspawn")
+ copyfile2(config.nspawn_settings, config.output_dir_or_cwd() / f"{name}.nspawn")
# If we're booting a directory image that wasn't built by root, we always make an ephemeral
# copy to avoid ending up with files not owned by the directory image owner in the
import itertools
import logging
import os
-import shutil
import subprocess
import sys
import tempfile
from mkosi.partition import Partition
from mkosi.run import CompletedProcess, run, workdir
from mkosi.sandbox import umask
-from mkosi.util import _FILE, PathString, StrEnum, flatten
+from mkosi.util import _FILE, PathString, StrEnum, copyfile2, flatten
from mkosi.versioncomp import GenericVersion
rel = output.relative_to(context.root)
log_step(f"Installing signed grub EFI binary from /{signed.relative_to(context.root)} to /{rel}")
- shutil.copy2(signed, output)
+ copyfile2(signed, output)
else:
if context.config.secure_boot and context.config.shim_bootloader != ShimBootloader.none:
if not (signed := find_signed_grub_image(context)):
for d in ("grub", "grub2"):
unicode = context.root / "usr/share" / d / "unicode.pf2"
if unicode.exists():
- shutil.copy2(unicode, dst)
+ copyfile2(unicode, dst)
def grub_bios_setup(context: Context, partitions: Sequence[Partition]) -> None:
output = output.with_name(f"{left_stem}.efi")
log_step(f"Installing signed {name} EFI binary from /{rel} to /{output}")
- shutil.copy2(p, context.root / output)
+ copyfile2(p, context.root / output)
return
if context.config.bootable == ConfigFeature.enabled:
sign_efi_binary(context, p, context.root / output)
else:
log_step(f"Installing unsigned {name} EFI binary /{rel} to /{output}")
- shutil.copy2(p, context.root / output)
+ copyfile2(p, context.root / output)
return
Path(context.root / "efi/loader/random-seed").unlink(missing_ok=True)
if context.config.shim_bootloader != ShimBootloader.none:
- shutil.copy2(
+ copyfile2(
context.root / f"efi/EFI/systemd/systemd-boot{context.config.architecture.to_efi()}.efi",
context.root / shim_second_stage_binary(context),
)
# SPDX-License-Identifier: LGPL-2.1-or-later
-import shutil
from collections.abc import Iterable
from mkosi.config import Architecture, Config
from mkosi.installer import PackageManager
from mkosi.installer.apk import Apk, ApkRepository
from mkosi.log import complete_step, die
+from mkosi.util import copyfile
class Installer(DistributionInstaller, distribution=Distribution.postmarketos):
if dest.exists():
continue
- shutil.copy2(key, dest)
+ copyfile(key, dest)
Apk.setup(context, list(cls.repositories(context)))
from mkosi.run import find_binary, run, uncaught_exception_handler
from mkosi.sandbox import __version__, umask
from mkosi.tree import copy_tree, move_tree, rmtree
-from mkosi.util import PathString, mandatory_variable, resource_path
+from mkosi.util import PathString, copyfile2, mandatory_variable, resource_path
@dataclasses.dataclass(frozen=True)
(Path(sandbox_tree) / "etc" / p).parent.mkdir(parents=True, exist_ok=True)
if (Path("/etc") / p).resolve().is_file():
- shutil.copy2(Path("/etc") / p, Path(sandbox_tree) / "etc" / p)
+ copyfile2(Path("/etc") / p, Path(sandbox_tree) / "etc" / p)
else:
shutil.copytree(
Path("/etc") / p,
# SPDX-License-Identifier: LGPL-2.1-or-later
import dataclasses
-import shutil
from collections.abc import Sequence
from pathlib import Path
from mkosi.installer import PackageManager
from mkosi.run import CompletedProcess, run, workdir
from mkosi.tree import rmtree
-from mkosi.util import _FILE, PathString
+from mkosi.util import _FILE, PathString, copyfile
@dataclasses.dataclass(frozen=True)
) # fmt: skip
keys = context.sandbox_tree / "etc/apk/keys"
keys.mkdir(parents=True, exist_ok=True)
- shutil.copy2(pub, keys / pub.name)
+ copyfile(pub, keys / pub.name)
run(
[
from mkosi.util import (
PathString,
StrEnum,
+ copyfile,
flock,
flock_or_die,
groupby,
)
if not vars.exists():
die(f"Firmware variables file {vars} does not exist")
- shutil.copy(vars, ovmf_vars)
+ copyfile(vars, ovmf_vars)
return ovmf_vars, ovmf_vars_format
F_DUPFD = 0
F_GETFD = 1
FD_CLOEXEC = 1
+FICLONE = 0x40049409
FS_IOC_GETFLAGS = 0x80086601
FS_IOC_SETFLAGS = 0x40086602
FS_NOCOW_FL = 0x00800000
btrfs_subvol_ioctl(path, BTRFS_IOC_SNAP_DESTROY_V2)
+def reflink(src: str, dst: str) -> None:
+ with (
+ close(os.open(src, os.O_CLOEXEC | os.O_RDONLY)) as src_fd,
+ close(os.open(dst, os.O_CLOEXEC | os.O_WRONLY | os.O_CREAT | os.O_TRUNC)) as dst_fd,
+ ):
+ if libc.ioctl(dst_fd, FICLONE, src_fd) < 0:
+ oserror("ioctl", src)
+
+
def join_new_session_keyring() -> None:
libkeyutils = ctypes.CDLL("libkeyutils.so.1")
if libkeyutils is None:
import os
import re
import resource
+import shutil
import stat
import tempfile
from collections.abc import Hashable, Iterable, Iterator, Mapping, Sequence
from mkosi.log import die
from mkosi.resources import as_file
+from mkosi.sandbox import reflink
T = TypeVar("T")
V = TypeVar("V")
return os.environ[name]
except KeyError:
die(f"${name} must be set in the environment")
+
+
+def copyfile(src: PathString, dst: PathString) -> PathString:
+ if os.path.isdir(dst):
+ dst = os.path.join(dst, os.path.basename(src))
+
+ try:
+ reflink(os.fspath(src), os.fspath(dst))
+ except OSError as e:
+ if e.errno not in (errno.EBADF, errno.EINVAL, errno.EXDEV, errno.ENOTTY, errno.EOPNOTSUPP):
+ raise e
+
+ shutil.copyfile(src, dst)
+
+ shutil.copymode(src, dst)
+ return dst
+
+
+def copyfile2(src: PathString, dst: PathString) -> PathString:
+ dst = copyfile(src, dst)
+ shutil.copystat(src, dst)
+ return dst