commit: 4df7daf31c225910ef711aa980e9822c37018ed9 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: Wed May 27 06:22:51 2020 +0000 URL: https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=4df7daf3
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..21e974b1 --- /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)}")