debian-forge/osbuild/util/mnt.py
Gianluca Zuccarelli 462c498dcf util/mnt: add explicit rw option
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.
2024-02-21 17:55:37 +01:00

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()