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 30, 2023
1 parent e0c151f commit 0b1def6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 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(state.source_date_epoch, state.root, config)

# 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(state.source_date_epoch, state.root, efi)

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(state.source_date_epoch, state.root, initrd)

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(state.source_date_epoch, state.root, kdst / "vmlinuz")
kmods = Path(shutil.copy2(kmods, kdst / "kmods"))
normalize_mtime(state.source_date_epoch, state.root, kdst / "kmods")

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(state.source_date_epoch, state.root, config)

# 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(state.source_date_epoch, state.root, dst)

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(state.source_date_epoch, dst)

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

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


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(state.source_date_epoch, state.root, boot_binary)

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(state.source_date_epoch, state.root, state.root / state.config.output_split_kernel)

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.source_date_epoch, state.root)
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(source_date_epoch: Optional[int], root: Path, target: Optional[Path] = None) -> None:
if source_date_epoch is None:
return

# If no target is given, normalize the mtime recursively for the entire tree, starting at root.
if not target:
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)
return

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

# Normalize the mtime of target and all its parents up to root.
with complete_step(f"Setting mtime to SOURCE_DATE_EPOCH for {target.relative_to(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
13 changes: 13 additions & 0 deletions 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 @@ -22,6 +23,18 @@ def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path) -> Non
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")
if source_date_epoch:
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: Optional[int] = mtime_epoch
else:
self.source_date_epoch = None

@property
def root(self) -> Path:
return self.workspace / "root"
Expand Down

0 comments on commit 0b1def6

Please sign in to comment.