Skip to content

Commit

Permalink
normalize mtime
Browse files Browse the repository at this point in the history
If set, the time stamp from SOURCE_DATE_EPOCH is used to normalize
mtime of files. We also need to pass the environment trough when
mkosi is invoking itself.

Co-authored-by: Malte Poll <mp@edgeless.systems>
  • Loading branch information
katexochen and malt3 committed Aug 29, 2023
1 parent e0c151f commit b77c548
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
39 changes: 39 additions & 0 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ def prepare_grub_config(state: MkosiState) -> Optional[Path]:
if not config.exists():
with umask(~0o600), config.open("w") as f:
f.write("set timeout=0\n")
normalize_mtime_path(state.root, config, state.source_date_epoch)

# Signed EFI grub shipped by distributions reads its configuration from /EFI/<distribution>/grub.cfg in
# the ESP so let's put a shim there to redirect to the actual configuration file.
Expand All @@ -670,6 +671,7 @@ def prepare_grub_config(state: MkosiState) -> Optional[Path]:

# Read the actual config file from the root of the ESP.
efi.write_text(f"configfile /{prefix}/grub.cfg\n")
normalize_mtime_path(state.root, efi, state.source_date_epoch)

return config

Expand Down Expand Up @@ -720,6 +722,7 @@ def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> Non
dst.mkdir(exist_ok=True)

initrd = Path(shutil.copy2(initrd, dst / "initrd"))
normalize_mtime_path(state.root, initrd, state.source_date_epoch)

with config.open("a") as f:
f.write('if [ "${grub_platform}" == "pc" ]; then\n')
Expand All @@ -733,7 +736,9 @@ def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> Non

with umask(~0o600):
kimg = Path(shutil.copy2(state.root / kimg, kdst / "vmlinuz"))
normalize_mtime_path(state.root, kdst / "vmlinuz", state.source_date_epoch)
kmods = Path(shutil.copy2(kmods, kdst / "kmods"))
normalize_mtime_path(state.root, kdst / "kmods", state.source_date_epoch)

f.write(
textwrap.dedent(
Expand All @@ -747,6 +752,7 @@ def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> Non
)

f.write('fi\n')
normalize_mtime_path(state.root, config, state.source_date_epoch)

# grub-install insists on opening the root partition device to probe it's filesystem which requires root
# so we're forced to reimplement its functionality. Luckily that's pretty simple, run grub-mkimage to
Expand All @@ -765,6 +771,7 @@ def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> Non

dst = state.root / "efi" / prefix / "i386-pc"
dst.mkdir(parents=True, exist_ok=True)
normalize_mtime_path(state.root, dst, state.source_date_epoch)

bwrap([mkimage,
"--directory", directory,
Expand All @@ -791,14 +798,17 @@ def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> Non

shutil.copy2(directory / "modinfo.sh", dst)
shutil.copy2(directory / "boot.img", dst)
normalize_mtime(dst, state.source_date_epoch)

dst = state.root / "efi" / prefix / "fonts"
dst.mkdir()
normalize_mtime_path(state.root, dst, state.source_date_epoch)

for prefix in ("grub", "grub2"):
unicode = state.root / "usr/share" / prefix / "unicode.pf2"
if unicode.exists():
shutil.copy2(unicode, dst)
normalize_mtime(dst, state.source_date_epoch)


def install_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> None:
Expand Down Expand Up @@ -934,6 +944,8 @@ def build_initrd(state: MkosiState) -> Path:
"--make-initrd", "yes",
"--bootable", "no",
"--manifest-format", "",
*([f"--environment=SOURCE_DATE_EPOCH={state.config.environment['SOURCE_DATE_EPOCH']}"]
if "SOURCE_DATE_EPOCH" in state.config.environment else []),
*(["--locale", state.config.locale] if state.config.locale else []),
*(["--locale-messages", state.config.locale_messages] if state.config.locale_messages else []),
*(["--keymap", state.config.keymap] if state.config.keymap else []),
Expand Down Expand Up @@ -1123,6 +1135,7 @@ def install_unified_kernel(state: MkosiState, partitions: Sequence[Partition]) -
boot_binary.parent.mkdir(parents=True, exist_ok=True)

run(cmd)
normalize_mtime_path(state.root, boot_binary, state.source_date_epoch)

if not (state.staging / state.config.output_split_uki).exists():
shutil.copy(boot_binary, state.staging / state.config.output_split_uki)
Expand Down Expand Up @@ -1150,6 +1163,7 @@ def install_unified_kernel(state: MkosiState, partitions: Sequence[Partition]) -
python = "python3" if state.config.tools_tree else os.getenv("MKOSI_INTERPRETER", "python3")

run([python], input=pefile)
normalize_mtime_path(state.root, state.root / state.config.output_split_kernel, state.source_date_epoch)

print_output_size(boot_binary)

Expand Down Expand Up @@ -1813,6 +1827,7 @@ def build_image(args: MkosiArgs, config: MkosiConfig) -> None:
run_selinux_relabel(state)
run_finalize_script(state)

normalize_mtime(state.root, state.source_date_epoch)
partitions = make_image(state, skip=("esp", "xbootldr"))
install_unified_kernel(state, partitions)
prepare_grub_efi(state)
Expand Down Expand Up @@ -2255,3 +2270,27 @@ def run_verb(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> None:

if args.verb == Verb.serve:
run_serve(last)


def normalize_mtime(root: Path, source_date_epoch: Optional[int]) -> None:
if source_date_epoch is None:
return
with complete_step("Setting mtime to SOURCE_DATE_EPOCH"):
os.utime(root, (source_date_epoch, source_date_epoch), follow_symlinks=False)
for p in root.rglob("*"):
os.utime(p, (source_date_epoch, source_date_epoch), follow_symlinks=False)


def normalize_mtime_path(root: Path, target: Path, source_date_epoch: Optional[int]) -> None:
if source_date_epoch is None:
return

if root not in target.parents:
raise ValueError(f"{target} is not a child of {root}")

with complete_step(f"Setting mtime to SOURCE_DATE_EPOCH for {root}"):
os.utime(target, (source_date_epoch, source_date_epoch), follow_symlinks=False)
for p in target.parents:
os.utime(p, (source_date_epoch, source_date_epoch), follow_symlinks=False)
if p == root:
break
2 changes: 1 addition & 1 deletion mkosi/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def extract_tar(src: Path, dst: Path, log: bool = True) -> None:
)


def make_cpio(src: Path, dst: Path, files: Optional[Iterable[Path]] = None) -> None:
def make_cpio(src: Path, dst: Path,files: Optional[Iterable[Path]] = None) -> None:
if not files:
files = src.rglob("*")

Expand Down
12 changes: 11 additions & 1 deletion mkosi/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1+

from pathlib import Path
from typing import Optional

from mkosi.config import MkosiArgs, MkosiConfig
from mkosi.tree import make_tree
Expand All @@ -9,7 +10,6 @@

class MkosiState:
"""State related properties."""

def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path) -> None:
self.args = args
self.config = config
Expand All @@ -21,6 +21,16 @@ def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path) -> Non
self.pkgmngr.mkdir()
self.install_dir.mkdir(exist_ok=True)
self.cache_dir.mkdir(parents=True, exist_ok=True)
source_date_epoch = self.config.environment.get("SOURCE_DATE_EPOCH")
self.source_date_epoch: Optional[int] = None
if source_date_epoch is not None:
try:
mtime_epoch = int(source_date_epoch)
except ValueError:
raise ValueError(f"SOURCE_DATE_EPOCH={source_date_epoch} is not a valid integer")
if mtime_epoch < 0:
raise ValueError(f"SOURCE_DATE_EPOCH={source_date_epoch} is negative")
self.source_date_epoch = mtime_epoch

@property
def root(self) -> Path:
Expand Down

0 comments on commit b77c548

Please sign in to comment.