]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Introduce flock_or_die() and use it in various places 2507/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 14 Mar 2024 13:54:45 +0000 (14:54 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 14 Mar 2024 14:00:48 +0000 (15:00 +0100)
Let's avoid weird error cases caused by two instances of mkosi trying
to currently do stuff with the same output by taking a BSD lock when
trying to do something with the output.

mkosi/__init__.py
mkosi/qemu.py
mkosi/util.py
mkosi/vmspawn.py

index c2f480e17bc469eb4cc0bc5ec598d23f884ff4d9..b754bd7dab7c38cc31f980974c47968bd7ea98a5 100644 (file)
@@ -72,6 +72,7 @@ from mkosi.user import CLONE_NEWNS, INVOKING_USER, become_root, unshare
 from mkosi.util import (
     flatten,
     flock,
+    flock_or_die,
     format_rlimit,
     make_executable,
     one_zero,
@@ -3633,7 +3634,7 @@ def run_shell(args: Args, config: Config) -> None:
         if config.ephemeral:
             fname = stack.enter_context(copy_ephemeral(config, config.output_dir_or_cwd() / config.output))
         else:
-            fname = config.output_dir_or_cwd() / config.output
+            fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output))
 
         if config.output_format == OutputFormat.disk and args.verb == Verb.boot:
             run(
@@ -3955,7 +3956,12 @@ def run_clean(args: Args, config: Config, *, resources: Path) -> None:
         remove_package_cache = args.force > 2
 
     if outputs := list(config.output_dir_or_cwd().glob(f"{config.output}*")):
-        with complete_step(f"Removing output files of {config.name()} image…"):
+        with (
+            complete_step(f"Removing output files of {config.name()} image…"),
+            flock_or_die(config.output_dir_or_cwd() / config.output)
+            if (config.output_dir_or_cwd() / config.output).exists()
+            else contextlib.nullcontext()
+        ):
             rmtree(*outputs)
 
     if remove_build_cache:
index 3c67dca821036b92f73743f7f72cf6399506b915..4239dde74193bc847266f34b16fc7392f8bee429 100644 (file)
@@ -40,7 +40,7 @@ from mkosi.run import AsyncioThread, find_binary, fork_and_wait, run, spawn
 from mkosi.tree import copy_tree, rmtree
 from mkosi.types import PathString
 from mkosi.user import INVOKING_USER, become_root
-from mkosi.util import StrEnum, flatten
+from mkosi.util import StrEnum, flatten, flock, flock_or_die
 from mkosi.versioncomp import GenericVersion
 
 QEMU_KVM_DEVICE_VERSION = GenericVersion("9.0")
@@ -426,7 +426,8 @@ def copy_ephemeral(config: Config, src: Path) -> Iterator[Path]:
                 sandbox=config.sandbox,
             )
 
-        fork_and_wait(copy)
+        with flock(src):
+            fork_and_wait(copy)
         yield tmp
     finally:
         def rm() -> None:
@@ -705,7 +706,7 @@ def run_qemu(args: Args, config: Config) -> None:
                 copy_ephemeral(config, config.output_dir_or_cwd() / config.output_with_compression)
             )
         else:
-            fname = config.output_dir_or_cwd() / config.output_with_compression
+            fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output_with_compression))
 
         if config.output_format == OutputFormat.disk and config.runtime_size:
             run(
index 0e3c598e877d3558c47f5e8d8046819a02b89403..812c33e53464cb939cd9fabdb77d33c488061290 100644 (file)
@@ -4,6 +4,7 @@ import ast
 import contextlib
 import copy
 import enum
+import errno
 import fcntl
 import functools
 import importlib
@@ -20,6 +21,7 @@ from pathlib import Path
 from types import ModuleType
 from typing import Any, Callable, Optional, TypeVar, no_type_check
 
+from mkosi.log import die
 from mkosi.types import PathString
 
 T = TypeVar("T")
@@ -142,6 +144,19 @@ def flock(path: Path, flags: int = fcntl.LOCK_EX) -> Iterator[int]:
         os.close(fd)
 
 
+@contextlib.contextmanager
+def flock_or_die(path: Path) -> Iterator[Path]:
+    try:
+        with flock(path, fcntl.LOCK_EX|fcntl.LOCK_NB):
+            yield path
+    except OSError as e:
+        if e.errno != errno.EWOULDBLOCK:
+            raise e
+
+        die(f"Cannot lock {path} as it is locked by another process",
+            hint="Maybe another mkosi process is still using it?")
+
+
 @contextlib.contextmanager
 def scopedenv(env: Mapping[str, Any]) -> Iterator[None]:
     old = copy.deepcopy(os.environ)
index d4fa3716d9dbd2daca036708eb1032340d065933..8ed0fe879bbade88de2284bf5b2ec67c296346de 100644 (file)
@@ -21,6 +21,7 @@ from mkosi.qemu import (
 )
 from mkosi.run import run
 from mkosi.types import PathString
+from mkosi.util import flock_or_die
 
 
 def run_vmspawn(args: Args, config: Config) -> None:
@@ -79,7 +80,7 @@ def run_vmspawn(args: Args, config: Config) -> None:
         if config.ephemeral:
             fname = stack.enter_context(copy_ephemeral(config, config.output_dir_or_cwd() / config.output))
         else:
-            fname = config.output_dir_or_cwd() / config.output
+            fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output))
 
         if config.output_format == OutputFormat.disk and config.runtime_size:
             run(