richardmaw-codethink pushed to branch richardmaw/fix-chroot-sandbox-devices at BuildStream / buildstream
Commits:
6 changed files:
- buildstream/_fuse/hardlinks.py
- buildstream/_fuse/mount.py
- buildstream/sandbox/_mount.py
- buildstream/sandbox/_sandboxchroot.py
- + tests/integration/project/elements/integration.bst
- tests/integration/shell.py
Changes:
... | ... | @@ -42,9 +42,10 @@ from .mount import Mount |
42 | 42 |
#
|
43 | 43 |
class SafeHardlinks(Mount):
|
44 | 44 |
|
45 |
- def __init__(self, directory, tempdir):
|
|
45 |
+ def __init__(self, directory, tempdir, fuse_mount_options={}):
|
|
46 | 46 |
self.directory = directory
|
47 | 47 |
self.tempdir = tempdir
|
48 |
+ super().__init__(fuse_mount_options=fuse_mount_options)
|
|
48 | 49 |
|
49 | 50 |
def create_operations(self):
|
50 | 51 |
return SafeHardlinkOps(self.directory, self.tempdir)
|
... | ... | @@ -87,6 +87,9 @@ class Mount(): |
87 | 87 |
# User Facing API #
|
88 | 88 |
################################################
|
89 | 89 |
|
90 |
+ def __init__(self, fuse_mount_options={}):
|
|
91 |
+ self._fuse_mount_options = fuse_mount_options
|
|
92 |
+ |
|
90 | 93 |
# mount():
|
91 | 94 |
#
|
92 | 95 |
# User facing API for mounting a fuse subclass implementation
|
... | ... | @@ -184,7 +187,8 @@ class Mount(): |
184 | 187 |
# Run fuse in foreground in this child process, internally libfuse
|
185 | 188 |
# will handle SIGTERM and gracefully exit it's own little main loop.
|
186 | 189 |
#
|
187 |
- FUSE(self.__operations, self.__mountpoint, nothreads=True, foreground=True, nonempty=True)
|
|
190 |
+ FUSE(self.__operations, self.__mountpoint, nothreads=True, foreground=True, nonempty=True,
|
|
191 |
+ **self._fuse_mount_options)
|
|
188 | 192 |
|
189 | 193 |
# Explicit 0 exit code, if the operations crashed for some reason, the exit
|
190 | 194 |
# code will not be 0, and we want to know about it.
|
... | ... | @@ -30,7 +30,7 @@ from .._fuse import SafeHardlinks |
30 | 30 |
# Helper data object representing a single mount point in the mount map
|
31 | 31 |
#
|
32 | 32 |
class Mount():
|
33 |
- def __init__(self, sandbox, mount_point, safe_hardlinks):
|
|
33 |
+ def __init__(self, sandbox, mount_point, safe_hardlinks, fuse_mount_options={}):
|
|
34 | 34 |
scratch_directory = sandbox._get_scratch_directory()
|
35 | 35 |
# Getting _get_underlying_directory() here is acceptable as
|
36 | 36 |
# we're part of the sandbox code. This will fail if our
|
... | ... | @@ -39,6 +39,7 @@ class Mount(): |
39 | 39 |
|
40 | 40 |
self.mount_point = mount_point
|
41 | 41 |
self.safe_hardlinks = safe_hardlinks
|
42 |
+ self._fuse_mount_options = fuse_mount_options
|
|
42 | 43 |
|
43 | 44 |
# FIXME: When the criteria for mounting something and it's parent
|
44 | 45 |
# mount is identical, then there is no need to mount an additional
|
... | ... | @@ -82,7 +83,7 @@ class Mount(): |
82 | 83 |
@contextmanager
|
83 | 84 |
def mounted(self, sandbox):
|
84 | 85 |
if self.safe_hardlinks:
|
85 |
- mount = SafeHardlinks(self.mount_origin, self.mount_tempdir)
|
|
86 |
+ mount = SafeHardlinks(self.mount_origin, self.mount_tempdir, self._fuse_mount_options)
|
|
86 | 87 |
with mount.mounted(self.mount_source):
|
87 | 88 |
yield
|
88 | 89 |
else:
|
... | ... | @@ -100,12 +101,12 @@ class Mount(): |
100 | 101 |
#
|
101 | 102 |
class MountMap():
|
102 | 103 |
|
103 |
- def __init__(self, sandbox, root_readonly):
|
|
104 |
+ def __init__(self, sandbox, root_readonly, fuse_mount_options={}):
|
|
104 | 105 |
# We will be doing the mounts in the order in which they were declared.
|
105 | 106 |
self.mounts = OrderedDict()
|
106 | 107 |
|
107 | 108 |
# We want safe hardlinks on rootfs whenever root is not readonly
|
108 |
- self.mounts['/'] = Mount(sandbox, '/', not root_readonly)
|
|
109 |
+ self.mounts['/'] = Mount(sandbox, '/', not root_readonly, fuse_mount_options)
|
|
109 | 110 |
|
110 | 111 |
for mark in sandbox._get_marked_directories():
|
111 | 112 |
directory = mark['directory']
|
... | ... | @@ -113,7 +114,7 @@ class MountMap(): |
113 | 114 |
|
114 | 115 |
# We want safe hardlinks for any non-root directory where
|
115 | 116 |
# artifacts will be staged to
|
116 |
- self.mounts[directory] = Mount(sandbox, directory, artifact)
|
|
117 |
+ self.mounts[directory] = Mount(sandbox, directory, artifact, fuse_mount_options)
|
|
117 | 118 |
|
118 | 119 |
# get_mount_source()
|
119 | 120 |
#
|
... | ... | @@ -35,6 +35,9 @@ from . import Sandbox, SandboxFlags |
35 | 35 |
|
36 | 36 |
|
37 | 37 |
class SandboxChroot(Sandbox):
|
38 |
+ |
|
39 |
+ _FUSE_MOUNT_OPTIONS = {'dev': True}
|
|
40 |
+ |
|
38 | 41 |
def __init__(self, *args, **kwargs):
|
39 | 42 |
super().__init__(*args, **kwargs)
|
40 | 43 |
|
... | ... | @@ -67,7 +70,8 @@ class SandboxChroot(Sandbox): |
67 | 70 |
|
68 | 71 |
# Create the mount map, this will tell us where
|
69 | 72 |
# each mount point needs to be mounted from and to
|
70 |
- self.mount_map = MountMap(self, flags & SandboxFlags.ROOT_READ_ONLY)
|
|
73 |
+ self.mount_map = MountMap(self, flags & SandboxFlags.ROOT_READ_ONLY,
|
|
74 |
+ self._FUSE_MOUNT_OPTIONS)
|
|
71 | 75 |
root_mount_source = self.mount_map.get_mount_source('/')
|
72 | 76 |
|
73 | 77 |
# Create a sysroot and run the command inside it
|
1 |
+kind: manual
|
|
2 |
+depends:
|
|
3 |
+- base.bst
|
|
4 |
+ |
|
5 |
+public:
|
|
6 |
+ bst:
|
|
7 |
+ integration-commands:
|
|
8 |
+ - |
|
|
9 |
+ echo noise >/dev/null
|
... | ... | @@ -342,3 +342,13 @@ def test_sysroot_workspace_visible(cli, tmpdir, datafiles): |
342 | 342 |
])
|
343 | 343 |
assert result.exit_code == 0
|
344 | 344 |
assert result.output == workspace_hello
|
345 |
+ |
|
346 |
+ |
|
347 |
+# Test system integration commands can access devices in /dev
|
|
348 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
349 |
+def test_integration_devices(cli, tmpdir, datafiles):
|
|
350 |
+ project = os.path.join(datafiles.dirname, datafiles.basename)
|
|
351 |
+ element_name = 'integration.bst'
|
|
352 |
+ |
|
353 |
+ result = execute_shell(cli, project, ["true"], element=element_name)
|
|
354 |
+ assert result.exit_code == 0
|