summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
authorMaxim Cournoyer <maxim.cournoyer@gmail.com>2019-07-14 20:50:23 +0900
committerMaxim Cournoyer <maxim.cournoyer@gmail.com>2020-05-20 08:30:35 -0400
commitb460ba7992a0b4af2ddb5927dcf062784539ef7b (patch)
tree4d77d01574da9a7aedf31dc3f16e94d82fa31adb /gnu
parentfa35fb58c84d1c1741e4e63c0b37074e35ed2a61 (diff)
bootloader: grub: Allow booting from a Btrfs subvolume.
* gnu/bootloader/grub.scm (strip-mount-point): Remove procedure. (normalize-file): Add procedure. (grub-configuration-file): New BTRFS-SUBVOLUME-FILE-NAME parameter. When defined, prepend its value to the kernel and initrd file names, using the NORMALIZE-FILE procedure. Adjust the call to EYE-CANDY to pass the BTRFS-SUBVOLUME-FILE-NAME argument. Normalize the KEYMAP file as well. (eye-candy): Add a BTRFS-SUBVOLUME-FILE-NAME parameter, and use it, along with the NORMALIZE-FILE procedure, to normalize the FONT-FILE and IMAGE nested variables. Adjust doc. * gnu/bootloader/depthcharge.scm (depthcharge-configuration-file): Adapt. * gnu/bootloader/extlinux.scm (extlinux-configuration-file): Likewise. * gnu/system/file-systems.scm (btrfs-subvolume?) (btrfs-store-subvolume-file-name): New procedures. * gnu/system.scm (operating-system-bootcfg): Specify the Btrfs subvolume file name the store resides on to the `operating-system-bootcfg' procedure, using the new BTRFS-SUBVOLUME-FILE-NAME argument. * doc/guix.texi (File Systems): Add a Btrfs subsection to document the use of subvolumes. * gnu/tests/install.scm (%btrfs-root-on-subvolume-os) (%btrfs-root-on-subvolume-os-source) (%btrfs-root-on-subvolume-installation-script) (%test-btrfs-root-on-subvolume-os): New variables.
Diffstat (limited to 'gnu')
-rw-r--r--gnu/bootloader/depthcharge.scm3
-rw-r--r--gnu/bootloader/extlinux.scm3
-rw-r--r--gnu/bootloader/grub.scm123
-rw-r--r--gnu/system.scm9
-rw-r--r--gnu/system/file-systems.scm55
-rw-r--r--gnu/tests/install.scm94
6 files changed, 236 insertions, 51 deletions
diff --git a/gnu/bootloader/depthcharge.scm b/gnu/bootloader/depthcharge.scm
index 58cc3f39322..0a50374bd9b 100644
--- a/gnu/bootloader/depthcharge.scm
+++ b/gnu/bootloader/depthcharge.scm
@@ -82,7 +82,8 @@
82(define* (depthcharge-configuration-file config entries 82(define* (depthcharge-configuration-file config entries
83 #:key 83 #:key
84 (system (%current-system)) 84 (system (%current-system))
85 (old-entries '())) 85 (old-entries '())
86 #:allow-other-keys)
86 (match entries 87 (match entries
87 ((entry) 88 ((entry)
88 (let ((kernel (menu-entry-linux entry)) 89 (let ((kernel (menu-entry-linux entry))
diff --git a/gnu/bootloader/extlinux.scm b/gnu/bootloader/extlinux.scm
index 5b4dd849656..6b5ff298e74 100644
--- a/gnu/bootloader/extlinux.scm
+++ b/gnu/bootloader/extlinux.scm
@@ -28,7 +28,8 @@
28(define* (extlinux-configuration-file config entries 28(define* (extlinux-configuration-file config entries
29 #:key 29 #:key
30 (system (%current-system)) 30 (system (%current-system))
31 (old-entries '())) 31 (old-entries '())
32 #:allow-other-keys)
32 "Return the U-Boot configuration file corresponding to CONFIG, a 33 "Return the U-Boot configuration file corresponding to CONFIG, a
33<u-boot-configuration> object, and where the store is available at STORE-FS, a 34<u-boot-configuration> object, and where the store is available at STORE-FS, a
34<file-system> object. OLD-ENTRIES is taken to be a list of menu entries 35<file-system> object. OLD-ENTRIES is taken to be a list of menu entries
diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm
index fb871c6e965..bb40c551a74 100644
--- a/gnu/bootloader/grub.scm
+++ b/gnu/bootloader/grub.scm
@@ -58,18 +58,29 @@
58;;; 58;;;
59;;; Code: 59;;; Code:
60 60
61(define (strip-mount-point mount-point file) 61(define* (normalize-file file mount-point btrfs-subvolume-file-name)
62 "Strip MOUNT-POINT from FILE, which is a gexp or other lowerable object 62 "Strip MOUNT-POINT and prepend BTRFS-SUBVOLUME-FILE-NAME to FILE, a
63denoting a file name." 63G-expression or other lowerable object denoting a file name."
64 (match mount-point 64
65 ((? string? mount-point) 65 (define (strip-mount-point mount-point file)
66 (if (string=? mount-point "/") 66 (if mount-point
67 file 67 (if (string=? mount-point "/")
68 #~(let ((file #$file)) 68 file
69 (if (string-prefix? #$mount-point file) 69 #~(let ((file #$file))
70 (substring #$file #$(string-length mount-point)) 70 (if (string-prefix? #$mount-point file)
71 file)))) 71 (substring #$file #$(string-length mount-point))
72 (#f file))) 72 file)))
73 file))
74
75 (define (prepend-btrfs-subvolume-file-name btrfs-subvolume-file-name file)
76 (if btrfs-subvolume-file-name
77 #~(string-append #$btrfs-subvolume-file-name #$file)
78 file))
79
80 (prepend-btrfs-subvolume-file-name btrfs-subvolume-file-name
81 (strip-mount-point mount-point file)))
82
83
73 84
74(define-record-type* <grub-theme> 85(define-record-type* <grub-theme>
75 ;; Default theme contributed by Felipe López. 86 ;; Default theme contributed by Felipe López.
@@ -124,13 +135,14 @@ file with the resolution provided in CONFIG."
124 (_ #f))))) 135 (_ #f)))))
125 136
126(define* (eye-candy config store-device store-mount-point 137(define* (eye-candy config store-device store-mount-point
127 #:key system port) 138 #:key btrfs-store-subvolume-file-name system port)
128 "Return a gexp that writes to PORT (a port-valued gexp) the 139 "Return a gexp that writes to PORT (a port-valued gexp) the 'grub.cfg' part
129'grub.cfg' part concerned with graphics mode, background images, colors, and 140concerned with graphics mode, background images, colors, and all that.
130all that. STORE-DEVICE designates the device holding the store, and 141STORE-DEVICE designates the device holding the store, and STORE-MOUNT-POINT is
131STORE-MOUNT-POINT is its mount point; these are used to determine where the 142its mount point; these are used to determine where the background image and
132background image and fonts must be searched for. SYSTEM must be the target 143fonts must be searched for. SYSTEM must be the target system string---e.g.,
133system string---e.g., \"x86_64-linux\"." 144\"x86_64-linux\". BTRFS-STORE-SUBVOLUME-FILE-NAME is the file name of the
145Btrfs subvolume, to be prepended to any store path, if any."
134 (define setup-gfxterm-body 146 (define setup-gfxterm-body
135 (let ((gfxmode 147 (let ((gfxmode
136 (or (and-let* ((theme (bootloader-configuration-theme config)) 148 (or (and-let* ((theme (bootloader-configuration-theme config))
@@ -167,11 +179,14 @@ fi~%" #+font-file)
167 (symbol->string (assoc-ref colors 'bg))))) 179 (symbol->string (assoc-ref colors 'bg)))))
168 180
169 (define font-file 181 (define font-file
170 (strip-mount-point store-mount-point 182 (normalize-file (file-append grub "/share/grub/unicode.pf2")
171 (file-append grub "/share/grub/unicode.pf2"))) 183 store-mount-point
184 btrfs-store-subvolume-file-name))
172 185
173 (define image 186 (define image
174 (grub-background-image config)) 187 (normalize-file (grub-background-image config)
188 store-mount-point
189 btrfs-store-subvolume-file-name))
175 190
176 (and image 191 (and image
177 #~(format #$port " 192 #~(format #$port "
@@ -196,7 +211,7 @@ fi~%"
196 #$(setup-gfxterm config font-file) 211 #$(setup-gfxterm config font-file)
197 #$(grub-setup-io config) 212 #$(grub-setup-io config)
198 213
199 #$(strip-mount-point store-mount-point image) 214 #$image
200 #$(theme-colors grub-theme-color-normal) 215 #$(theme-colors grub-theme-color-normal)
201 #$(theme-colors grub-theme-color-highlight)))) 216 #$(theme-colors grub-theme-color-highlight))))
202 217
@@ -304,52 +319,66 @@ code."
304(define* (grub-configuration-file config entries 319(define* (grub-configuration-file config entries
305 #:key 320 #:key
306 (system (%current-system)) 321 (system (%current-system))
307 (old-entries '())) 322 (old-entries '())
323 btrfs-subvolume-file-name)
308 "Return the GRUB configuration file corresponding to CONFIG, a 324 "Return the GRUB configuration file corresponding to CONFIG, a
309<bootloader-configuration> object, and where the store is available at 325<bootloader-configuration> object, and where the store is available at
310STORE-FS, a <file-system> object. OLD-ENTRIES is taken to be a list of menu 326STORE-FS, a <file-system> object. OLD-ENTRIES is taken to be a list
311entries corresponding to old generations of the system." 327of menu entries corresponding to old generations of the system.
328BTRFS-SUBVOLUME-FILE-NAME may be used to specify on which subvolume a
329Btrfs root file system resides."
312 (define all-entries 330 (define all-entries
313 (append entries (bootloader-configuration-menu-entries config))) 331 (append entries (bootloader-configuration-menu-entries config)))
314 (define (menu-entry->gexp entry) 332 (define (menu-entry->gexp entry)
315 (let ((device (menu-entry-device entry)) 333 (let* ((device (menu-entry-device entry))
316 (device-mount-point (menu-entry-device-mount-point entry)) 334 (device-mount-point (menu-entry-device-mount-point entry))
317 (label (menu-entry-label entry)) 335 (label (menu-entry-label entry))
318 (kernel (menu-entry-linux entry)) 336 (arguments (menu-entry-linux-arguments entry))
319 (arguments (menu-entry-linux-arguments entry)) 337 (kernel (normalize-file (menu-entry-linux entry)
320 (initrd (menu-entry-initrd entry))) 338 device-mount-point
339 btrfs-subvolume-file-name))
340 (initrd (normalize-file (menu-entry-initrd entry)
341 device-mount-point
342 btrfs-subvolume-file-name)))
321 ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point. 343 ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point.
322 ;; Use the right file names for KERNEL and INITRD in case 344 ;; Use the right file names for KERNEL and INITRD in case
323 ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a 345 ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a
324 ;; separate partition. 346 ;; separate partition.
325 (let ((kernel (strip-mount-point device-mount-point kernel)) 347
326 (initrd (strip-mount-point device-mount-point initrd))) 348 ;; When BTRFS-SUBVOLUME-FILE-NAME is defined, prepend it the kernel and
327 #~(format port "menuentry ~s { 349 ;; initrd paths, to allow booting from a Btrfs subvolume.
350 #~(format port "menuentry ~s {
328 ~a 351 ~a
329 linux ~a ~a 352 linux ~a ~a
330 initrd ~a 353 initrd ~a
331}~%" 354}~%"
332 #$label 355 #$label
333 #$(grub-root-search device kernel) 356 #$(grub-root-search device kernel)
334 #$kernel (string-join (list #$@arguments)) 357 #$kernel (string-join (list #$@arguments))
335 #$initrd)))) 358 #$initrd)))
336 (define sugar 359 (define sugar
337 (eye-candy config 360 (eye-candy config
338 (menu-entry-device (first all-entries)) 361 (menu-entry-device (first all-entries))
339 (menu-entry-device-mount-point (first all-entries)) 362 (menu-entry-device-mount-point (first all-entries))
363 #:btrfs-store-subvolume-file-name btrfs-subvolume-file-name
340 #:system system 364 #:system system
341 #:port #~port)) 365 #:port #~port))
342 366
343 (define keyboard-layout-config 367 (define keyboard-layout-config
344 (let ((layout (bootloader-configuration-keyboard-layout config)) 368 (let* ((layout (bootloader-configuration-keyboard-layout config))
345 (grub (bootloader-package 369 (grub (bootloader-package
346 (bootloader-configuration-bootloader config)))) 370 (bootloader-configuration-bootloader config)))
347 #~(let ((keymap #$(and layout 371 (keymap* (and layout
348 (keyboard-layout-file layout #:grub grub)))) 372 (keyboard-layout-file layout #:grub grub)))
349 (when keymap 373 (keymap (and keymap*
350 (format port "\ 374 (if btrfs-subvolume-file-name
375 #~(string-append #$btrfs-subvolume-file-name
376 #$keymap*)
377 keymap*))))
378 #~(when #$keymap
379 (format port "\
351insmod keylayouts 380insmod keylayouts
352keymap ~a~%" keymap))))) 381keymap ~a~%" #$keymap))))
353 382
354 (define builder 383 (define builder
355 #~(call-with-output-file #$output 384 #~(call-with-output-file #$output
diff --git a/gnu/system.scm b/gnu/system.scm
index cd75e4d4baf..d929187695f 100644
--- a/gnu/system.scm
+++ b/gnu/system.scm
@@ -8,6 +8,7 @@
8;;; Copyright © 2020 Danny Milosavljevic <dannym@scratchpost.org> 8;;; Copyright © 2020 Danny Milosavljevic <dannym@scratchpost.org>
9;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re> 9;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
10;;; Copyright © 2020 Florian Pelz <pelzflorian@pelzflorian.de> 10;;; Copyright © 2020 Florian Pelz <pelzflorian@pelzflorian.de>
11;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
11;;; 12;;;
12;;; This file is part of GNU Guix. 13;;; This file is part of GNU Guix.
13;;; 14;;;
@@ -1102,19 +1103,23 @@ entry."
1102(define* (operating-system-bootcfg os #:optional (old-entries '())) 1103(define* (operating-system-bootcfg os #:optional (old-entries '()))
1103 "Return the bootloader configuration file for OS. Use OLD-ENTRIES, 1104 "Return the bootloader configuration file for OS. Use OLD-ENTRIES,
1104a list of <menu-entry>, to populate the \"old entries\" menu." 1105a list of <menu-entry>, to populate the \"old entries\" menu."
1105 (let* ((root-fs (operating-system-root-file-system os)) 1106 (let* ((file-systems (operating-system-file-systems os))
1107 (root-fs (operating-system-root-file-system os))
1106 (root-device (file-system-device root-fs)) 1108 (root-device (file-system-device root-fs))
1107 (params (operating-system-boot-parameters 1109 (params (operating-system-boot-parameters
1108 os root-device 1110 os root-device
1109 #:system-kernel-arguments? #t)) 1111 #:system-kernel-arguments? #t))
1110 (entry (boot-parameters->menu-entry params)) 1112 (entry (boot-parameters->menu-entry params))
1111 (bootloader-conf (operating-system-bootloader os))) 1113 (bootloader-conf (operating-system-bootloader os)))
1114
1112 (define generate-config-file 1115 (define generate-config-file
1113 (bootloader-configuration-file-generator 1116 (bootloader-configuration-file-generator
1114 (bootloader-configuration-bootloader bootloader-conf))) 1117 (bootloader-configuration-bootloader bootloader-conf)))
1115 1118
1116 (generate-config-file bootloader-conf (list entry) 1119 (generate-config-file bootloader-conf (list entry)
1117 #:old-entries old-entries))) 1120 #:old-entries old-entries
1121 #:btrfs-subvolume-file-name
1122 (btrfs-store-subvolume-file-name file-systems))))
1118 1123
1119(define* (operating-system-boot-parameters os root-device 1124(define* (operating-system-boot-parameters os root-device
1120 #:key system-kernel-arguments?) 1125 #:key system-kernel-arguments?)
diff --git a/gnu/system/file-systems.scm b/gnu/system/file-systems.scm
index 07f272db7c4..0f94577760a 100644
--- a/gnu/system/file-systems.scm
+++ b/gnu/system/file-systems.scm
@@ -22,7 +22,10 @@
22 #:use-module (ice-9 match) 22 #:use-module (ice-9 match)
23 #:use-module (rnrs bytevectors) 23 #:use-module (rnrs bytevectors)
24 #:use-module (srfi srfi-1) 24 #:use-module (srfi srfi-1)
25 #:use-module (srfi srfi-2)
25 #:use-module (srfi srfi-9) 26 #:use-module (srfi srfi-9)
27 #:use-module (srfi srfi-26)
28 #:use-module (srfi srfi-35)
26 #:use-module (srfi srfi-9 gnu) 29 #:use-module (srfi srfi-9 gnu)
27 #:use-module (guix records) 30 #:use-module (guix records)
28 #:use-module (gnu system uuid) 31 #:use-module (gnu system uuid)
@@ -49,6 +52,8 @@
49 file-system-location 52 file-system-location
50 53
51 file-system-type-predicate 54 file-system-type-predicate
55 btrfs-subvolume?
56 btrfs-store-subvolume-file-name
52 57
53 file-system-label 58 file-system-label
54 file-system-label? 59 file-system-label?
@@ -566,4 +571,54 @@ system has the given TYPE."
566 (lambda (fs) 571 (lambda (fs)
567 (string=? (file-system-type fs) type))) 572 (string=? (file-system-type fs) type)))
568 573
574
575;;;
576;;; Btrfs specific helpers.
577;;;
578
579(define (btrfs-subvolume? fs)
580 "Predicate to check if FS, a file-system object, is a Btrfs subvolume."
581 (and-let* ((btrfs-file-system? (string= "btrfs" (file-system-type fs)))
582 (option-keys (map (match-lambda
583 ((key . value) key)
584 (key key))
585 (file-system-options->alist
586 (file-system-options fs)))))
587 (find (cut string-prefix? "subvol" <>) option-keys)))
588
589(define (btrfs-store-subvolume-file-name file-systems)
590 "Return the subvolume file name within the Btrfs top level onto which the
591store is located, else #f."
592
593 (define (prepend-slash/maybe s)
594 (if (string=? "/" (string-take s 1))
595 s
596 (string-append "/" s)))
597
598 (define (file-name-depth file-name)
599 (length (string-tokenize file-name %not-slash)))
600
601 (and-let* ((btrfs-subvolume-fs (filter btrfs-subvolume? file-systems))
602 (btrfs-subvolume-fs*
603 (sort btrfs-subvolume-fs
604 (lambda (fs1 fs2)
605 (> (file-name-depth (file-system-mount-point fs1))
606 (file-name-depth (file-system-mount-point fs2))))))
607 (store-subvolume-fs
608 (find (lambda (fs) (file-prefix? (file-system-mount-point fs)
609 (%store-prefix)))
610 btrfs-subvolume-fs*))
611 (options (file-system-options->alist
612 (file-system-options store-subvolume-fs))))
613 ;; XXX: Deriving the subvolume name based from a subvolume ID is not
614 ;; supported, as we'd need to query the actual file system.
615 (or (and=> (assoc-ref options "subvol") prepend-slash/maybe)
616 ;; FIXME: Use &fix-hint once it no longer pulls in (guix utils).
617 (raise (condition
618 (&message
619 (message "The store is on a Btrfs subvolume, but the \
620subvolume name is unknown.
621Hint: Use the \"subvol\" Btrfs file system option.")))))))
622
623
569;;; file-systems.scm ends here 624;;; file-systems.scm ends here
diff --git a/gnu/tests/install.scm b/gnu/tests/install.scm
index 94d970e1cc5..cea26c8ef3b 100644
--- a/gnu/tests/install.scm
+++ b/gnu/tests/install.scm
@@ -61,6 +61,7 @@
61 %test-raid-root-os 61 %test-raid-root-os
62 %test-encrypted-root-os 62 %test-encrypted-root-os
63 %test-btrfs-root-os 63 %test-btrfs-root-os
64 %test-btrfs-root-on-subvolume-os
64 %test-jfs-root-os 65 %test-jfs-root-os
65 %test-f2fs-root-os 66 %test-f2fs-root-os
66 67
@@ -865,6 +866,99 @@ build (current-guix) and then store a couple of full system images.")
865 866
866 867
867;;; 868;;;
869;;; Btrfs root file system on a subvolume.
870;;;
871
872(define-os-with-source (%btrfs-root-on-subvolume-os
873 %btrfs-root-on-subvolume-os-source)
874 ;; The OS we want to install.
875 (use-modules (gnu) (gnu tests) (srfi srfi-1))
876
877 (operating-system
878 (host-name "hurd")
879 (timezone "America/Montreal")
880 (locale "en_US.UTF-8")
881 (bootloader (bootloader-configuration
882 (bootloader grub-bootloader)
883 (target "/dev/vdb")))
884 (kernel-arguments '("console=ttyS0"))
885 (file-systems (cons* (file-system
886 (device (file-system-label "btrfs-pool"))
887 (mount-point "/")
888 (options "subvol=rootfs,compress=zstd")
889 (type "btrfs"))
890 (file-system
891 (device (file-system-label "btrfs-pool"))
892 (mount-point "/home")
893 (options "subvol=homefs,compress=lzo")
894 (type "btrfs"))
895 %base-file-systems))
896 (users (cons (user-account
897 (name "charlie")
898 (group "users")
899 (supplementary-groups '("wheel" "audio" "video")))
900 %base-user-accounts))
901 (services (cons (service marionette-service-type
902 (marionette-configuration
903 (imported-modules '((gnu services herd)
904 (guix combinators)))))
905 %base-services))))
906
907(define %btrfs-root-on-subvolume-installation-script
908 ;; Shell script of a simple installation.
909 "\
910. /etc/profile
911set -e -x
912guix --version
913
914export GUIX_BUILD_OPTIONS=--no-grafts
915ls -l /run/current-system/gc-roots
916parted --script /dev/vdb mklabel gpt \\
917 mkpart primary ext2 1M 3M \\
918 mkpart primary ext2 3M 2G \\
919 set 1 boot on \\
920 set 1 bios_grub on
921
922# Setup the top level Btrfs file system with its subvolume.
923mkfs.btrfs -L btrfs-pool /dev/vdb2
924mount /dev/vdb2 /mnt
925btrfs subvolume create /mnt/rootfs
926btrfs subvolume create /mnt/homefs
927umount /dev/vdb2
928
929# Mount the subvolumes, ready for installation.
930mount LABEL=btrfs-pool -o 'subvol=rootfs,compress=zstd' /mnt
931mkdir /mnt/home
932mount LABEL=btrfs-pool -o 'subvol=homefs,compress=zstd' /mnt/home
933
934herd start cow-store /mnt
935mkdir /mnt/etc
936cp /etc/target-config.scm /mnt/etc/config.scm
937guix system build /mnt/etc/config.scm
938guix system init /mnt/etc/config.scm /mnt --no-substitutes
939sync
940reboot\n")
941
942(define %test-btrfs-root-on-subvolume-os
943 (system-test
944 (name "btrfs-root-on-subvolume-os")
945 (description
946 "Test basic functionality of an OS installed like one would do by hand.
947This test is expensive in terms of CPU and storage usage since we need to
948build (current-guix) and then store a couple of full system images.")
949 (value
950 (mlet* %store-monad
951 ((image
952 (run-install %btrfs-root-on-subvolume-os
953 %btrfs-root-on-subvolume-os-source
954 #:script
955 %btrfs-root-on-subvolume-installation-script))
956 (command (qemu-command/writable-image image)))
957 (run-basic-test %btrfs-root-on-subvolume-os command
958 "btrfs-root-on-subvolume-os")))))
959
960
961;;;
868;;; JFS root file system. 962;;; JFS root file system.
869;;; 963;;;
870 964