summaryrefslogtreecommitdiff
path: root/src/buildstream/sandbox/_mounter.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildstream/sandbox/_mounter.py')
-rw-r--r--src/buildstream/sandbox/_mounter.py147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/buildstream/sandbox/_mounter.py b/src/buildstream/sandbox/_mounter.py
new file mode 100644
index 000000000..e6054c20d
--- /dev/null
+++ b/src/buildstream/sandbox/_mounter.py
@@ -0,0 +1,147 @@
+#
+# Copyright (C) 2017 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+#
+# Authors:
+# Tristan Maat <tristan.maat@codethink.co.uk>
+
+import sys
+from contextlib import contextmanager
+
+from .._exceptions import SandboxError
+from .. import utils, _signals
+
+
+# A class to wrap the `mount` and `umount` system commands
+class Mounter():
+ @classmethod
+ def _mount(cls, dest, src=None, mount_type=None,
+ stdout=sys.stdout, stderr=sys.stderr, options=None,
+ flags=None):
+
+ argv = [utils.get_host_tool('mount')]
+ if mount_type:
+ argv.extend(['-t', mount_type])
+ if options:
+ argv.extend(['-o', options])
+ if flags:
+ argv.extend(flags)
+
+ if src is not None:
+ argv += [src]
+ argv += [dest]
+
+ status, _ = utils._call(
+ argv,
+ terminate=True,
+ stdout=stdout,
+ stderr=stderr
+ )
+
+ if status != 0:
+ raise SandboxError('`{}` failed with exit code {}'
+ .format(' '.join(argv), status))
+
+ return dest
+
+ @classmethod
+ def _umount(cls, path, stdout=sys.stdout, stderr=sys.stderr):
+
+ cmd = [utils.get_host_tool('umount'), '-R', path]
+ status, _ = utils._call(
+ cmd,
+ terminate=True,
+ stdout=stdout,
+ stderr=stderr
+ )
+
+ if status != 0:
+ raise SandboxError('`{}` failed with exit code {}'
+ .format(' '.join(cmd), status))
+
+ # mount()
+ #
+ # A wrapper for the `mount` command. The device is unmounted when
+ # the context is left.
+ #
+ # Args:
+ # dest (str) - The directory to mount to
+ # src (str) - The directory to mount
+ # stdout (file) - stdout
+ # stderr (file) - stderr
+ # mount_type (str|None) - The mount type (can be omitted or None)
+ # kwargs - Arguments to pass to the mount command, such as `ro=True`
+ #
+ # Yields:
+ # (str) The path to the destination
+ #
+ @classmethod
+ @contextmanager
+ def mount(cls, dest, src=None, stdout=sys.stdout,
+ stderr=sys.stderr, mount_type=None, **kwargs):
+
+ def kill_proc():
+ cls._umount(dest, stdout, stderr)
+
+ options = ','.join([key for key, val in kwargs.items() if val])
+
+ path = cls._mount(dest, src, mount_type, stdout=stdout, stderr=stderr, options=options)
+ try:
+ with _signals.terminator(kill_proc):
+ yield path
+ finally:
+ cls._umount(dest, stdout, stderr)
+
+ # bind_mount()
+ #
+ # Mount a directory to a different location (a hardlink for all
+ # intents and purposes). The directory is unmounted when the
+ # context is left.
+ #
+ # Args:
+ # dest (str) - The directory to mount to
+ # src (str) - The directory to mount
+ # stdout (file) - stdout
+ # stderr (file) - stderr
+ # kwargs - Arguments to pass to the mount command, such as `ro=True`
+ #
+ # Yields:
+ # (str) The path to the destination
+ #
+ # While this is equivalent to `mount --rbind`, this option may not
+ # exist and can be dangerous, requiring careful cleanupIt is
+ # recommended to use this function over a manual mount invocation.
+ #
+ @classmethod
+ @contextmanager
+ def bind_mount(cls, dest, src=None, stdout=sys.stdout,
+ stderr=sys.stderr, **kwargs):
+
+ def kill_proc():
+ cls._umount(dest, stdout, stderr)
+
+ kwargs['rbind'] = True
+ options = ','.join([key for key, val in kwargs.items() if val])
+
+ path = cls._mount(dest, src, None, stdout, stderr, options)
+
+ try:
+ with _signals.terminator(kill_proc):
+ # Make the rbind a slave to avoid unmounting vital devices in
+ # /proc
+ cls._mount(dest, flags=['--make-rslave'])
+ yield path
+ finally:
+ cls._umount(dest, stdout, stderr)