summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2016-05-30 22:44:58 +0200
committerLudovic Courtès <ludo@gnu.org>2016-05-31 00:11:04 +0200
commitc06f6db7a424fd47e3cd2625dbfda2367316f3bd (patch)
tree025d18dd2ed6d4f6a62cc09aa9633161c7d7edc8
parent4c14d4eaa7ee9d5d89c04a41adb50c7c532d14e1 (diff)
container: Gracefully report mount errors in the child process.
Fixes <http://bugs.gnu.org/23306>. * gnu/build/linux-container.scm (run-container): Use 'socketpair' instead of 'pipe'. Rename 'in' to 'child' and 'out' to 'parent'. Send a 'ready message or an exception argument list from the child to the parent; adjust the parent accordingly. * tests/containers.scm ("call-with-container, mnt namespace, wrong bind mount"): New test. * tests/guix-environment-container.sh: Add test with --expose=/does-not-exist.
-rw-r--r--gnu/build/linux-container.scm42
-rw-r--r--tests/containers.scm12
-rw-r--r--tests/guix-environment-container.sh10
3 files changed, 52 insertions, 12 deletions
diff --git a/gnu/build/linux-container.scm b/gnu/build/linux-container.scm
index 562d50bcc78..91996d06caf 100644
--- a/gnu/build/linux-container.scm
+++ b/gnu/build/linux-container.scm
@@ -205,35 +205,53 @@ host user identifiers to map into the user namespace."
205 ;; The parent process must initialize the user namespace for the child 205 ;; The parent process must initialize the user namespace for the child
206 ;; before it can boot. To negotiate this, a pipe is used such that the 206 ;; before it can boot. To negotiate this, a pipe is used such that the
207 ;; child process blocks until the parent writes to it. 207 ;; child process blocks until the parent writes to it.
208 (match (pipe) 208 (match (socketpair PF_UNIX SOCK_STREAM 0)
209 ((in . out) 209 ((child . parent)
210 (let ((flags (namespaces->bit-mask namespaces))) 210 (let ((flags (namespaces->bit-mask namespaces)))
211 (match (clone flags) 211 (match (clone flags)
212 (0 212 (0
213 (call-with-clean-exit 213 (call-with-clean-exit
214 (lambda () 214 (lambda ()
215 (close out) 215 (close-port parent)
216 ;; Wait for parent to set things up. 216 ;; Wait for parent to set things up.
217 (match (read in) 217 (match (read child)
218 ('ready 218 ('ready
219 (close in)
220 (purify-environment) 219 (purify-environment)
221 (when (memq 'mnt namespaces) 220 (when (memq 'mnt namespaces)
222 (mount-file-systems root mounts 221 (catch #t
223 #:mount-/proc? (memq 'pid namespaces) 222 (lambda ()
224 #:mount-/sys? (memq 'net namespaces))) 223 (mount-file-systems root mounts
224 #:mount-/proc? (memq 'pid namespaces)
225 #:mount-/sys? (memq 'net
226 namespaces)))
227 (lambda args
228 ;; Forward the exception to the parent process.
229 (write args child)
230 (primitive-exit 3))))
225 ;; TODO: Manage capabilities. 231 ;; TODO: Manage capabilities.
232 (write 'ready child)
233 (close-port child)
226 (thunk)) 234 (thunk))
227 (_ ;parent died or something 235 (_ ;parent died or something
228 (primitive-exit 2)))))) 236 (primitive-exit 2))))))
229 (pid 237 (pid
238 (close-port child)
230 (when (memq 'user namespaces) 239 (when (memq 'user namespaces)
231 (initialize-user-namespace pid host-uids)) 240 (initialize-user-namespace pid host-uids))
232 ;; TODO: Initialize cgroups. 241 ;; TODO: Initialize cgroups.
233 (close in) 242 (write 'ready parent)
234 (write 'ready out) 243 (newline parent)
235 (close out) 244
236 pid)))))) 245 ;; Check whether the child process' setup phase succeeded.
246 (let ((message (read parent)))
247 (close-port parent)
248 (match message
249 ('ready ;success
250 pid)
251 (((? symbol? key) args ...) ;exception
252 (apply throw key args))
253 (_ ;unexpected termination
254 #f)))))))))
237 255
238(define* (call-with-container mounts thunk #:key (namespaces %namespaces) 256(define* (call-with-container mounts thunk #:key (namespaces %namespaces)
239 (host-uids 1)) 257 (host-uids 1))
diff --git a/tests/containers.scm b/tests/containers.scm
index c11cdd1ce55..5a0f9937bb8 100644
--- a/tests/containers.scm
+++ b/tests/containers.scm
@@ -79,6 +79,18 @@
79 (assert-exit (file-exists? "/testing"))) 79 (assert-exit (file-exists? "/testing")))
80 #:namespaces '(user mnt)))) 80 #:namespaces '(user mnt))))
81 81
82(test-equal "call-with-container, mnt namespace, wrong bind mount"
83 `(system-error ,ENOENT)
84 ;; An exception should be raised; see <http://bugs.gnu.org/23306>.
85 (catch 'system-error
86 (lambda ()
87 (call-with-container '(("/does-not-exist" device "/foo"
88 "none" (bind-mount) #f #f))
89 (const #t)
90 #:namespaces '(user mnt)))
91 (lambda args
92 (list 'system-error (system-error-errno args)))))
93
82(test-assert "call-with-container, all namespaces" 94(test-assert "call-with-container, all namespaces"
83 (zero? 95 (zero?
84 (call-with-container '() 96 (call-with-container '()
diff --git a/tests/guix-environment-container.sh b/tests/guix-environment-container.sh
index 0a7ea481fca..5ea6c49263d 100644
--- a/tests/guix-environment-container.sh
+++ b/tests/guix-environment-container.sh
@@ -44,6 +44,16 @@ else
44 test $? = 42 44 test $? = 42
45fi 45fi
46 46
47# Make sure file-not-found errors in mounts are reported.
48if guix environment --container --ad-hoc --bootstrap guile-bootstrap \
49 --expose=/does-not-exist -- guile -c 1 2> "$tmpdir/error"
50then
51 false
52else
53 grep "/does-not-exist" "$tmpdir/error"
54 grep "[Nn]o such file" "$tmpdir/error"
55fi
56
47# Make sure that the right directories are mapped. 57# Make sure that the right directories are mapped.
48mount_test_code=" 58mount_test_code="
49(use-modules (ice-9 rdelim) 59(use-modules (ice-9 rdelim)