1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
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(object):
@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])
with _signals.terminator(kill_proc):
yield cls._mount(dest, src, mount_type, stdout=stdout, stderr=stderr, options=options)
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)
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
cls._umount(dest, stdout, stderr)
|