diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2016-05-30 22:44:58 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2016-05-31 00:11:04 +0200 |
| commit | c06f6db7a424fd47e3cd2625dbfda2367316f3bd (patch) | |
| tree | 025d18dd2ed6d4f6a62cc09aa9633161c7d7edc8 | |
| parent | 4c14d4eaa7ee9d5d89c04a41adb50c7c532d14e1 (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.scm | 42 | ||||
| -rw-r--r-- | tests/containers.scm | 12 | ||||
| -rw-r--r-- | tests/guix-environment-container.sh | 10 |
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 |
| 45 | fi | 45 | fi |
| 46 | 46 | ||
| 47 | # Make sure file-not-found errors in mounts are reported. | ||
| 48 | if guix environment --container --ad-hoc --bootstrap guile-bootstrap \ | ||
| 49 | --expose=/does-not-exist -- guile -c 1 2> "$tmpdir/error" | ||
| 50 | then | ||
| 51 | false | ||
| 52 | else | ||
| 53 | grep "/does-not-exist" "$tmpdir/error" | ||
| 54 | grep "[Nn]o such file" "$tmpdir/error" | ||
| 55 | fi | ||
| 56 | |||
| 47 | # Make sure that the right directories are mapped. | 57 | # Make sure that the right directories are mapped. |
| 48 | mount_test_code=" | 58 | mount_test_code=" |
| 49 | (use-modules (ice-9 rdelim) | 59 | (use-modules (ice-9 rdelim) |
