diff options
| author | Rutherther <rutherther@ditigal.xyz> | 2026-02-04 19:19:19 +0100 |
|---|---|---|
| committer | Maxim Cournoyer <maxim@guixotic.coop> | 2026-02-23 11:46:09 +0900 |
| commit | e3d8fc1147d03b6e6a47f8f1976068ad0faec9ab (patch) | |
| tree | 8bcde4a5676f5a83d3dee9dc2d3da68ef0c5e0f1 /gnu | |
| parent | d45da4a5e932aba8d59e08e29ff3db2a3c2179c3 (diff) | |
file-systems: mount-file-system: Guard against missing devices.
When a device with a UUID is missing, canonicalize-device-spec will throw an
error. This error is not handled for mount-may-fail? devices. That means
that if you use UUID device and it isn't available, the boot will hang on
the user-file-systems not being started. All user services depend on that
service.
Also added a test for this behavior.
* gnu/build/file-systems.scm
(mount-file-system): Guard canonicalize-device-spec call.
(canonicalize-device-spec): Throw &partition-lookup-error on missing
partition.
(&partition-lookup-error): New variable.
* gnu/tests/base.scm (%test-missing-file-system): New variable.
Change-Id: I3b8d652251cef421cff6d2fdafb8d9d7d1fc74b5
Reported-By: renbus, on IRC
Signed-off-by: Maxim Cournoyer <maxim@guixotic.coop>
Diffstat (limited to 'gnu')
| -rw-r--r-- | gnu/build/file-systems.scm | 80 | ||||
| -rw-r--r-- | gnu/tests/base.scm | 54 |
2 files changed, 107 insertions, 27 deletions
diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index d25b798b117..72d7f5f2601 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | #:use-module (guix i18n) | 37 | #:use-module (guix i18n) |
| 38 | #:use-module (rnrs io ports) | 38 | #:use-module (rnrs io ports) |
| 39 | #:use-module (rnrs bytevectors) | 39 | #:use-module (rnrs bytevectors) |
| 40 | #:use-module (ice-9 exceptions) | ||
| 40 | #:use-module (ice-9 match) | 41 | #:use-module (ice-9 match) |
| 41 | #:use-module (ice-9 rdelim) | 42 | #:use-module (ice-9 rdelim) |
| 42 | #:use-module (ice-9 regex) | 43 | #:use-module (ice-9 regex) |
| @@ -45,6 +46,8 @@ | |||
| 45 | #:autoload (system repl repl) (start-repl) | 46 | #:autoload (system repl repl) (start-repl) |
| 46 | #:use-module (srfi srfi-1) | 47 | #:use-module (srfi srfi-1) |
| 47 | #:use-module (srfi srfi-26) | 48 | #:use-module (srfi srfi-26) |
| 49 | #:use-module (srfi srfi-34) | ||
| 50 | #:use-module (srfi srfi-35) | ||
| 48 | #:export (disk-partitions | 51 | #:export (disk-partitions |
| 49 | partition-label-predicate | 52 | partition-label-predicate |
| 50 | partition-uuid-predicate | 53 | partition-uuid-predicate |
| @@ -65,6 +68,9 @@ | |||
| 65 | 68 | ||
| 66 | cleanly-unmounted-ext2? | 69 | cleanly-unmounted-ext2? |
| 67 | 70 | ||
| 71 | partition-lookup-error? | ||
| 72 | &partition-lookup-error | ||
| 73 | |||
| 68 | bind-mount | 74 | bind-mount |
| 69 | 75 | ||
| 70 | system*/tty | 76 | system*/tty |
| @@ -1182,6 +1188,10 @@ were found." | |||
| 1182 | (define find-partition-by-luks-uuid | 1188 | (define find-partition-by-luks-uuid |
| 1183 | (find-partition luks-partition-uuid-predicate)) | 1189 | (find-partition luks-partition-uuid-predicate)) |
| 1184 | 1190 | ||
| 1191 | (define-condition-type &partition-lookup-error &condition | ||
| 1192 | partition-lookup-error? | ||
| 1193 | (spec partition-lookup-error-spec)) | ||
| 1194 | |||
| 1185 | 1195 | ||
| 1186 | (define (canonicalize-device-spec spec) | 1196 | (define (canonicalize-device-spec spec) |
| 1187 | "Return the device name corresponding to SPEC, which can be a <uuid>, a | 1197 | "Return the device name corresponding to SPEC, which can be a <uuid>, a |
| @@ -1201,7 +1211,9 @@ file name or an nfs-root containing ':/')." | |||
| 1201 | ;; Some devices take a bit of time to appear, most notably USB | 1211 | ;; Some devices take a bit of time to appear, most notably USB |
| 1202 | ;; storage devices. Thus, wait for the device to appear. | 1212 | ;; storage devices. Thus, wait for the device to appear. |
| 1203 | (if (> count max-trials) | 1213 | (if (> count max-trials) |
| 1204 | (error "failed to resolve partition" (fmt spec)) | 1214 | (raise (condition |
| 1215 | (&partition-lookup-error | ||
| 1216 | (spec (fmt spec))))) | ||
| 1205 | (begin | 1217 | (begin |
| 1206 | (format #t "waiting for partition '~a' to appear...~%" | 1218 | (format #t "waiting for partition '~a' to appear...~%" |
| 1207 | (fmt spec)) | 1219 | (fmt spec)) |
| @@ -1319,6 +1331,14 @@ corresponds to the symbols listed in FLAGS." | |||
| 1319 | (() | 1331 | (() |
| 1320 | 0)))) | 1332 | 0)))) |
| 1321 | 1333 | ||
| 1334 | (define kind-and-args-exception? | ||
| 1335 | (exception-predicate &exception-with-kind-and-args)) | ||
| 1336 | |||
| 1337 | (define (system-error? exception) | ||
| 1338 | "Return true if EXCEPTION is a Guile 'system-error exception." | ||
| 1339 | (and (kind-and-args-exception? exception) | ||
| 1340 | (eq? 'system-error (exception-kind exception)))) | ||
| 1341 | |||
| 1322 | (define* (mount-file-system fs #:key (root "/root") | 1342 | (define* (mount-file-system fs #:key (root "/root") |
| 1323 | (check? (file-system-check? fs)) | 1343 | (check? (file-system-check? fs)) |
| 1324 | (skip-check-if-clean? | 1344 | (skip-check-if-clean? |
| @@ -1339,8 +1359,8 @@ corresponds to the symbols listed in FLAGS." | |||
| 1339 | (host-part (string-take source idx)) | 1359 | (host-part (string-take source idx)) |
| 1340 | ;; Strip [] from around host if present | 1360 | ;; Strip [] from around host if present |
| 1341 | (host (match (string-split host-part (string->char-set "[]")) | 1361 | (host (match (string-split host-part (string->char-set "[]")) |
| 1342 | (("" h "") h) | 1362 | (("" h "") h) |
| 1343 | ((h) h))) | 1363 | ((h) h))) |
| 1344 | (inet-addr (host-to-ip host "nfs"))) | 1364 | (inet-addr (host-to-ip host "nfs"))) |
| 1345 | ;; Mounting an NFS file system requires passing the address | 1365 | ;; Mounting an NFS file system requires passing the address |
| 1346 | ;; of the server in the addr= option | 1366 | ;; of the server in the addr= option |
| @@ -1426,26 +1446,37 @@ corresponds to the symbols listed in FLAGS." | |||
| 1426 | "," 'prefix) | 1446 | "," 'prefix) |
| 1427 | ""))))) | 1447 | ""))))) |
| 1428 | 1448 | ||
| 1429 | (let* ((type (file-system-type fs)) | 1449 | (guard (c ((partition-lookup-error? c) |
| 1430 | (source (canonicalize-device-spec (file-system-device fs))) | 1450 | (format (current-error-port) |
| 1431 | (target (string-append root "/" | 1451 | "could not find the partition: ~a~%" |
| 1432 | (file-system-mount-point fs))) | 1452 | (partition-lookup-error-spec c)) |
| 1433 | (flags (logior (mount-flags->bit-mask (file-system-flags fs)) | 1453 | (unless (file-system-mount-may-fail? fs) |
| 1434 | 1454 | (raise c))) | |
| 1435 | ;; For bind mounts, preserve the original flags such | 1455 | ((system-error? c) |
| 1436 | ;; as MS_NOSUID, etc. Failing to do that, the | 1456 | (format (current-error-port) |
| 1437 | ;; MS_REMOUNT call below fails with EPERM. | 1457 | "could not mount partition ~a: ~a~%" |
| 1438 | ;; See <https://bugs.gnu.org/46292> | 1458 | (file-system-device fs) |
| 1439 | (if (memq 'bind-mount (file-system-flags fs)) | 1459 | (exception-message c)) |
| 1440 | (statfs-flags->mount-flags | 1460 | (unless (file-system-mount-may-fail? fs) |
| 1441 | (file-system-mount-flags (statfs source))) | 1461 | (raise c)))) |
| 1442 | 0))) | 1462 | (let* ((type (file-system-type fs)) |
| 1443 | (options (file-system-options fs))) | 1463 | (source (canonicalize-device-spec (file-system-device fs))) |
| 1444 | (when check? | 1464 | (target (string-append root "/" |
| 1445 | (check-file-system source type (not skip-check-if-clean?) repair)) | 1465 | (file-system-mount-point fs))) |
| 1466 | (flags (logior (mount-flags->bit-mask (file-system-flags fs)) | ||
| 1467 | |||
| 1468 | ;; For bind mounts, preserve the original flags such | ||
| 1469 | ;; as MS_NOSUID, etc. Failing to do that, the | ||
| 1470 | ;; MS_REMOUNT call below fails with EPERM. | ||
| 1471 | ;; See <https://bugs.gnu.org/46292> | ||
| 1472 | (if (memq 'bind-mount (file-system-flags fs)) | ||
| 1473 | (statfs-flags->mount-flags | ||
| 1474 | (file-system-mount-flags (statfs source))) | ||
| 1475 | 0))) | ||
| 1476 | (options (file-system-options fs))) | ||
| 1477 | (when check? | ||
| 1478 | (check-file-system source type (not skip-check-if-clean?) repair)) | ||
| 1446 | 1479 | ||
| 1447 | (catch 'system-error | ||
| 1448 | (lambda () | ||
| 1449 | ;; Create the mount point. Most of the time this is a directory, but | 1480 | ;; Create the mount point. Most of the time this is a directory, but |
| 1450 | ;; in the case of a bind mount, a regular file or socket may be | 1481 | ;; in the case of a bind mount, a regular file or socket may be |
| 1451 | ;; needed. | 1482 | ;; needed. |
| @@ -1474,10 +1505,7 @@ corresponds to the symbols listed in FLAGS." | |||
| 1474 | (when (and (= MS_BIND (logand flags MS_BIND)) | 1505 | (when (and (= MS_BIND (logand flags MS_BIND)) |
| 1475 | (= MS_RDONLY (logand flags MS_RDONLY))) | 1506 | (= MS_RDONLY (logand flags MS_RDONLY))) |
| 1476 | (let ((flags (logior MS_REMOUNT flags))) | 1507 | (let ((flags (logior MS_REMOUNT flags))) |
| 1477 | (mount source target type flags options)))) | 1508 | (mount source target type flags options)))))) |
| 1478 | (lambda args | ||
| 1479 | (or (file-system-mount-may-fail? fs) | ||
| 1480 | (apply throw args)))))) | ||
| 1481 | 1509 | ||
| 1482 | (define %device-name-regexp "/dev/[hsvw]d([abcd])([0-9]*)") | 1510 | (define %device-name-regexp "/dev/[hsvw]d([abcd])([0-9]*)") |
| 1483 | (define %hurd-device-name-regexp "part:([0-9]*):device:[hw]d([0-9]*)") | 1511 | (define %hurd-device-name-regexp "part:([0-9]*):device:[hw]d([0-9]*)") |
diff --git a/gnu/tests/base.scm b/gnu/tests/base.scm index 86fa6374efc..403aa20cfff 100644 --- a/gnu/tests/base.scm +++ b/gnu/tests/base.scm | |||
| @@ -24,7 +24,8 @@ | |||
| 24 | #:use-module (gnu tests) | 24 | #:use-module (gnu tests) |
| 25 | #:use-module (gnu image) | 25 | #:use-module (gnu image) |
| 26 | #:use-module (gnu system) | 26 | #:use-module (gnu system) |
| 27 | #:autoload (gnu system image) (system-image) | 27 | #:use-module (gnu system file-systems) |
| 28 | #:autoload (gnu system image) (system-image qcow2-image-type) | ||
| 28 | #:use-module (gnu system privilege) | 29 | #:use-module (gnu system privilege) |
| 29 | #:use-module (gnu system shadow) | 30 | #:use-module (gnu system shadow) |
| 30 | #:use-module (gnu system vm) | 31 | #:use-module (gnu system vm) |
| @@ -61,6 +62,8 @@ | |||
| 61 | %test-cleanup | 62 | %test-cleanup |
| 62 | %test-activation | 63 | %test-activation |
| 63 | 64 | ||
| 65 | %test-missing-file-system | ||
| 66 | |||
| 64 | %hello-dependencies-manifest | 67 | %hello-dependencies-manifest |
| 65 | guix-daemon-test-cases | 68 | guix-daemon-test-cases |
| 66 | %test-guix-daemon | 69 | %test-guix-daemon |
| @@ -1357,3 +1360,52 @@ runs unprivileged.") | |||
| 1357 | #:imported-modules '((gnu services herd) | 1360 | #:imported-modules '((gnu services herd) |
| 1358 | (guix combinators))))) | 1361 | (guix combinators))))) |
| 1359 | (run-guix-daemon-test os "guix-daemon-unprivileged-test"))))) | 1362 | (run-guix-daemon-test os "guix-daemon-unprivileged-test"))))) |
| 1363 | |||
| 1364 | (define %test-missing-file-system | ||
| 1365 | (system-test | ||
| 1366 | (name "missing-file-system") | ||
| 1367 | (description | ||
| 1368 | "Test that boot does not fail when a file system that might fail | ||
| 1369 | is specified and isn't provided by any device.") | ||
| 1370 | (value | ||
| 1371 | (let* ((os (marionette-operating-system | ||
| 1372 | (operating-system | ||
| 1373 | (inherit %simple-os) | ||
| 1374 | (kernel-arguments (list "console=ttyS0,115200")) | ||
| 1375 | (file-systems | ||
| 1376 | (cons* (file-system | ||
| 1377 | (device (uuid "abcdef12-3456-7890-abcd-ef1234567890")) | ||
| 1378 | (mount-point "/somewhere/1") | ||
| 1379 | (mount? #t) | ||
| 1380 | (mount-may-fail? #t) | ||
| 1381 | (type "ext4")) | ||
| 1382 | (file-system | ||
| 1383 | (device (file-system-label "missing-fs")) | ||
| 1384 | (mount-point "/somewhere/2") | ||
| 1385 | (mount? #t) | ||
| 1386 | (mount-may-fail? #t) | ||
| 1387 | (type "ext4")) | ||
| 1388 | (file-system | ||
| 1389 | (device "/dev/missing") | ||
| 1390 | (mount-point "/somewhere/3") | ||
| 1391 | (mount? #t) | ||
| 1392 | (mount-may-fail? #t) | ||
| 1393 | (type "ext4")) | ||
| 1394 | (file-system | ||
| 1395 | (device (file-system-label "my-root")) | ||
| 1396 | (mount-point "/") | ||
| 1397 | (type "ext4")) | ||
| 1398 | %base-file-systems))) | ||
| 1399 | #:imported-modules '((gnu services herd) | ||
| 1400 | (guix combinators)))) | ||
| 1401 | (image (system-image (os->image os #:type qcow2-image-type))) | ||
| 1402 | (command | ||
| 1403 | #~`(,(string-append #$qemu-minimal "/bin/" (qemu-command)) | ||
| 1404 | ,@(if (file-exists? "/dev/kvm") | ||
| 1405 | '("-enable-kvm") | ||
| 1406 | '()) | ||
| 1407 | "-m" "1024" ;memory size, in MiB | ||
| 1408 | "-serial" "stdio" | ||
| 1409 | "-snapshot" ;for volatile root, writable overlay | ||
| 1410 | "-drive" ,(format #f "file=~a,if=virtio" #$image)))) | ||
| 1411 | (run-basic-test os command name))))) | ||
