Under certain conditions a bind mount without a specified `rw` or `ro` option gets mounted read-only. We need a way to be explicit about needing a rw mount. We might want to change this in the future to be a single option (mode optional?) with valid values "rw", "ro". It's not entirely clear what the conditions are but it occurs when bind mounting the containers storage into the osbuild store, which we will need for the next few commits.
86 lines
2.4 KiB
Python
86 lines
2.4 KiB
Python
"""Mount utilities
|
|
"""
|
|
|
|
import contextlib
|
|
import subprocess
|
|
|
|
|
|
def mount(source, target, bind=True, ro=True, private=True, mode="0755"):
|
|
options = []
|
|
if ro:
|
|
options += ["ro"]
|
|
if mode:
|
|
options += [mode]
|
|
|
|
args = []
|
|
if bind:
|
|
args += ["--rbind"]
|
|
if private:
|
|
args += ["--make-rprivate"]
|
|
if options:
|
|
args += ["-o", ",".join(options)]
|
|
|
|
r = subprocess.run(["mount"] + args + [source, target],
|
|
stderr=subprocess.STDOUT,
|
|
stdout=subprocess.PIPE,
|
|
encoding="utf-8",
|
|
check=False)
|
|
|
|
if r.returncode != 0:
|
|
code = r.returncode
|
|
msg = r.stdout.strip()
|
|
raise RuntimeError(f"{msg} (code: {code})")
|
|
|
|
|
|
def umount(target, lazy=False):
|
|
args = []
|
|
if lazy:
|
|
args += ["--lazy"]
|
|
subprocess.run(["sync", "-f", target], check=True)
|
|
subprocess.run(["umount", "-R"] + args + [target], check=True)
|
|
|
|
|
|
class MountGuard(contextlib.AbstractContextManager):
|
|
def __init__(self):
|
|
self.mounts = []
|
|
|
|
def mount(self, source, target, bind=True, ro=False, rw=False, mode="0755"):
|
|
options = []
|
|
if bind:
|
|
options += ["bind"]
|
|
if ro:
|
|
options += ["ro"]
|
|
if rw:
|
|
options += ["rw"]
|
|
if mode:
|
|
options += [mode]
|
|
|
|
args = ["--make-private"]
|
|
if options:
|
|
args += ["-o", ",".join(options)]
|
|
|
|
r = subprocess.run(["mount"] + args + [source, target],
|
|
stderr=subprocess.STDOUT,
|
|
stdout=subprocess.PIPE,
|
|
encoding="utf-8",
|
|
check=False)
|
|
if r.returncode != 0:
|
|
code = r.returncode
|
|
msg = r.stdout.strip()
|
|
raise RuntimeError(f"{msg} (code: {code})")
|
|
|
|
self.mounts += [{"source": source, "target": target}]
|
|
|
|
def umount(self):
|
|
|
|
while self.mounts:
|
|
mnt = self.mounts.pop() # FILO: get the last mount
|
|
target = mnt["target"]
|
|
# The sync should in theory not be needed but in rare
|
|
# cases `target is busy` error has been spotted.
|
|
# Calling `sync` does not hurt so we keep it for now.
|
|
subprocess.run(["sync", "-f", target], check=True)
|
|
subprocess.run(["umount", target], check=True)
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self.umount()
|