Skip to content

Commit f156f96

Browse files
committed
mkosi-initrd: protect old initrd image against errors
When `copy_tree()` ends up calling `cp`, if it fails because there is not enough space, it leaves and incomplete initrd image in the output directory. If we are writing the initrd image directly where a bootloader entry has an initrd configured, the system will fail to boot. So, backup the old initrd image before calling `copy_tree()`, and restore it if that function throws an exception.
1 parent e30b69c commit f156f96

File tree

1 file changed

+27
-6
lines changed

1 file changed

+27
-6
lines changed

mkosi/initrd.py

+27-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import platform
99
import shutil
10+
import subprocess
1011
import sys
1112
import tempfile
1213
from pathlib import Path
@@ -18,7 +19,7 @@
1819
from mkosi.log import log_notice, log_setup
1920
from mkosi.run import find_binary, run, uncaught_exception_handler
2021
from mkosi.sandbox import __version__, umask
21-
from mkosi.tree import copy_tree
22+
from mkosi.tree import copy_tree, rmtree
2223
from mkosi.util import PathString, mandatory_variable, resource_path
2324

2425

@@ -141,12 +142,32 @@ def initrd_finalize(staging_dir: str, output: str, output_dir: str) -> None:
141142
else:
142143
output_dir = os.fspath(Path.cwd())
143144

145+
# backup current image
146+
if Path(f"{output_dir}/{output}").exists():
147+
log_notice(f"Backup {output_dir}/{output} to {staging_dir}/{output}.bak")
148+
copy_tree(
149+
Path(f"{output_dir}/{output}"),
150+
Path(f"{staging_dir}/{output}.bak"),
151+
)
152+
144153
log_notice(f"Copying {staging_dir}/{output} to {output_dir}/{output}")
145-
# mkosi symlinks the expected output image, so dereference it
146-
copy_tree(
147-
Path(f"{staging_dir}/{output}").resolve(),
148-
Path(f"{output_dir}/{output}"),
149-
)
154+
try:
155+
# mkosi symlinks the expected output image, so dereference it
156+
copy_tree(
157+
Path(f"{staging_dir}/{output}").resolve(),
158+
Path(f"{output_dir}/{output}"),
159+
)
160+
except subprocess.CalledProcessError:
161+
# restore image backup on error
162+
if Path(f"{staging_dir}/{output}.bak").exists():
163+
log_notice(f"Removing incomplete {output_dir}/{output}")
164+
rmtree(Path(f"{output_dir}/{output}"))
165+
log_notice(f"Restoring backup {staging_dir}/{output}.bak to {output_dir}/{output}")
166+
copy_tree(
167+
Path(f"{staging_dir}/{output}.bak"),
168+
Path(f"{output_dir}/{output}"),
169+
)
170+
raise
150171

151172

152173
def initrd_common_args(parser: argparse.ArgumentParser) -> None:

0 commit comments

Comments
 (0)