commit: b11d30977dd90954357722f488e37ad3cd257ff4
Author: Matt Turner <mattst88 <AT> gentoo <DOT> org>
AuthorDate: Sat May 16 21:44:37 2020 +0000
Commit: Matt Turner <mattst88 <AT> gentoo <DOT> org>
CommitDate: Thu May 21 22:07:41 2020 +0000
URL: https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=b11d3097
catalyst: Add and use support API for handling mounts
Handle the mounts/unmounts in all in process rather than shelling out
(pun intended!) to an external program.
Signed-off-by: Matt Turner <mattst88 <AT> gentoo.org>
catalyst/base/stagebase.py | 28 ++++++++++++++++++----------
catalyst/mount.py | 32 ++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/catalyst/base/stagebase.py b/catalyst/base/stagebase.py
index 00efd252..aa5cafd0 100644
--- a/catalyst/base/stagebase.py
+++ b/catalyst/base/stagebase.py
@@ -15,6 +15,7 @@ from DeComp.compress import CompressMap
from catalyst import log
from catalyst.defaults import (confdefaults, MOUNT_DEFAULTS, PORT_LOGDIR_CLEAN)
+from catalyst.mount import MS, mount, umount
from catalyst.support import (CatalystError, file_locate, normpath,
cmd, read_makeconf, ismount, file_check)
from catalyst.base.targetbase import TargetBase
@@ -847,7 +848,9 @@ class StageBase(TargetBase, ClearBase, GenBase):
source = str(self.mount[x]['source'])
target = self.settings['chroot_path'] +
str(self.mount[x]['target'])
- mount = ['mount']
+ filesystem = ''
+ flags = MS.NONE
+ options = ''
log.debug('bind %s: "%s" -> "%s"', x, source, target)
@@ -855,18 +858,19 @@ class StageBase(TargetBase, ClearBase, GenBase):
if 'var_tmpfs_portage' not in self.settings:
continue
- mount += ['-t', 'tmpfs', '-o',
- f"size={self.settings['var_tmpfs_portage']}G"]
+ filesystem = 'tmpfs'
+ options = f"size={self.settings['var_tmpfs_portage']}G"
elif source == 'tmpfs':
- mount += ['-t', 'tmpfs']
+ filesystem = 'tmpfs'
elif source == 'shm':
- mount += ['-t', 'tmpfs', '-o', 'noexec,nosuid,nodev']
+ filesystem = 'tmpfs'
+ flags = MS.NOEXEC | MS.NOSUID | MS.NODEV
else:
source_path = Path(self.mount[x]['source'])
if source_path.suffix == '.sqfs':
- mount += ['-o', 'ro']
+ flags = MS.RDONLY
else:
- mount.append('--bind')
+ flags = MS.BIND
# We may need to create the source of the bind mount.
E.g., in the
# case of an empty package cache we must create the
directory that
@@ -875,7 +879,11 @@ class StageBase(TargetBase, ClearBase, GenBase):
Path(target).mkdir(mode=0o755, parents=True, exist_ok=True)
- cmd(mount + [source, target], env=self.env, fail_func=self.unbind)
+ try:
+ mount(source, target, filesystem, flags, options)
+ except OSError as e:
+ self.unbind()
+ raise CatalystError
def unbind(self):
ouch = 0
@@ -893,7 +901,7 @@ class StageBase(TargetBase, ClearBase, GenBase):
continue
try:
- cmd(['umount', target], env=self.env)
+ umount(target)
except CatalystError:
log.warning('First attempt to unmount failed: %s', target)
log.warning('Killing any pids still running in the chroot')
@@ -901,7 +909,7 @@ class StageBase(TargetBase, ClearBase, GenBase):
self.kill_chroot_pids()
try:
- cmd(['umount', target], env=self.env)
+ umount(target)
except CatalystError:
ouch = 1
log.warning("Couldn't umount bind mount: %s", target)
diff --git a/catalyst/mount.py b/catalyst/mount.py
new file mode 100644
index 00000000..58b953fc
--- /dev/null
+++ b/catalyst/mount.py
@@ -0,0 +1,32 @@
+import ctypes
+import ctypes.util
+import enum
+import os
+
+libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
+libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,
ctypes.c_ulong, ctypes.c_char_p)
+libc.umount.argtypes = (ctypes.c_char_p)
+
+class MS(enum.IntFlag):
+ NONE = 0x0
+ RDONLY = 0x1
+ NOSUID = 0x2
+ NODEV = 0x4
+ NOEXEC = 0x8
+ BIND = 0x1000
+
+def mount(source: str, target: str, fs: str, flags: MS = MS.NONE, options: str
= '') -> None:
+ ret = libc.mount(source.encode(), target.encode(), fs.encode(), flags,
+ options.encode())
+ if ret < 0:
+ errno = ctypes.get_errno()
+ raise OSError(errno,
+ f"Error mounting {source} ({fs}) on {target} with
options"
+ f" {options}': {os.strerror(errno)}")
+
+def umount(target: str) -> None:
+ ret = libc.umount(target.encode())
+ if ret < 0:
+ errno = ctypes.get_errno()
+ raise OSError(errno,
+ f"Error unmounting {target}': {os.strerror(errno)}"