From 6858efa540d89c54ce377bfa6a6882e551cd2e56 Mon Sep 17 00:00:00 2001 From: Maxim Cournoyer Date: Sun, 14 Jul 2019 20:50:23 +0900 Subject: [PATCH 1/4] bootloader: grub: Allow booting from a Btrfs subvolume. * gnu/bootloader/grub.scm (prepend-subvol, arguments->subvol): New procedures. (grub-configuration-file): Use ARGUMENTS->SUBVOL to extract the subvolume name from the kernel arguments, and prepend it to the kernel and initrd paths using the PREPEND-SUBVOL procedure. * tests/grub.scm: Add tests for the "arguments->subvol" procedure. * doc/guix.texi (File Systems, (Bootloader Configuration): Document the use of Btrfs subvolumes. --- doc/guix.texi | 29 ++++++++++++++++++++- gnu/bootloader/grub.scm | 43 +++++++++++++++++++++++++------ gnu/build/linux-boot.scm | 7 +++-- gnu/build/linux-modules.scm | 3 ++- tests/grub.scm | 51 +++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 tests/grub.scm diff --git a/doc/guix.texi b/doc/guix.texi index 707c2ba700..cc7c91ac92 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -48,7 +48,7 @@ Copyright @copyright{} 2017 humanitiesNerd@* Copyright @copyright{} 2017 Christopher Allan Webber@* Copyright @copyright{} 2017, 2018 Marius Bakke@* Copyright @copyright{} 2017 Hartmut Goebel@* -Copyright @copyright{} 2017 Maxim Cournoyer@* +Copyright @copyright{} 2017, 2019 Maxim Cournoyer@* Copyright @copyright{} 2017, 2018, 2019 Tobias Geerinckx-Rice@* Copyright @copyright{} 2017 George Clemmer@* Copyright @copyright{} 2017 Andy Wingo@* @@ -10802,6 +10802,20 @@ using the @code{file-system} form, like this: (type "ext4")) @end example +@cindex Btrfs subvolume, file system +Below is a more complex example, making use of a Btrfs subvolume, named +@code{rootfs}, which parent Btrfs file system is labeled @code{my-btrfs-pool}, +on an encrypted device (hence the dependency on @code{mapped-devices}): + +@example +(file-system + (device (file-system-label "my-btrfs-pool")) + (mount-point "/") + (type "btrfs") + (options "defaults,subvol=rootfs") + (dependencies mapped-devices)) +@end example + As usual, some of the fields are mandatory---those shown in the example above---while others can be omitted. These are described below. @@ -24868,6 +24882,19 @@ when you boot it on your system. @code{grub-bootloader} allows you to boot in particular Intel-based machines in ``legacy'' BIOS mode. +@cindex rootflags, Grub +@cindex Btrfs root subvolume, Grub +To allow using a Btrfs @emph{subvolume} for the root partition, the Grub-based +bootloaders can use a subvolume @emph{name} passed using the @code{rootflags} +kernel argument, e.g.: + +@example +(kernel-arguments '("rootflags=subvol=@var{root-subvolume-name}")) +@end example + +to correctly populate the @code{kernel} and @code{initrd} fields of the Grub +configuration file. + @cindex ARM, bootloaders @cindex AArch64, bootloaders Available bootloaders are described in @code{(gnu bootloader @dots{})} diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm index d984d5f5e3..dee6028dd0 100644 --- a/gnu/bootloader/grub.scm +++ b/gnu/bootloader/grub.scm @@ -3,6 +3,7 @@ ;;; Copyright © 2016 Chris Marusich ;;; Copyright © 2017 Leo Famulari ;;; Copyright © 2017 Mathieu Othacehe +;;; Copyright © 2019 Maxim Cournoyer ;;; ;;; This file is part of GNU Guix. ;;; @@ -25,6 +26,8 @@ #:use-module (guix gexp) #:use-module (gnu artwork) #:use-module (gnu bootloader) + #:use-module ((gnu build linux-modules) #:select (%not-comma)) + #:use-module ((gnu build linux-boot) #:select (find-long-option)) #:use-module (gnu system uuid) #:use-module (gnu system file-systems) #:use-module (gnu system keyboard) @@ -34,6 +37,7 @@ #:use-module (ice-9 match) #:use-module (ice-9 regex) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) #:export (grub-image grub-image? grub-image-aspect-ratio @@ -73,6 +77,14 @@ denoting a file name." file)))) (#f file))) +(define (prepend-subvol subvol file) + "Prepend SUBVOL from FILE, which is a gexp or other lowerable object +denoting a file name." + (match subvol + ((? string? subvol) + #~(string-append "/" #$subvol #$file)) + (#f file))) + (define-record-type* grub-image make-grub-image grub-image? @@ -313,6 +325,13 @@ code." ((or #f (? string?)) #~(format #f "search --file --set ~a" #$file))))) +(define (arguments->subvol arguments) + "Return any \"subvol\" value given as an option to the \"rootflags\" +argument, or #f on failure." + (let* ((rootflags (find-long-option "rootflags" arguments)) + (rootflags-options (and=> rootflags (cut string-tokenize <> %not-comma)))) + (and=> rootflags-options (cut find-long-option "subvol" <>)))) + (define* (grub-configuration-file config entries #:key (system (%current-system)) @@ -324,18 +343,26 @@ entries corresponding to old generations of the system." (define all-entries (append entries (bootloader-configuration-menu-entries config))) (define (menu-entry->gexp entry) - (let ((device (menu-entry-device entry)) - (device-mount-point (menu-entry-device-mount-point entry)) - (label (menu-entry-label entry)) - (kernel (menu-entry-linux entry)) - (arguments (menu-entry-linux-arguments entry)) - (initrd (menu-entry-initrd entry))) + (let* ((device (menu-entry-device entry)) + (device-mount-point (menu-entry-device-mount-point entry)) + (label (menu-entry-label entry)) + (kernel (menu-entry-linux entry)) + (arguments (menu-entry-linux-arguments entry)) + (subvol (arguments->subvol arguments)) + (initrd (menu-entry-initrd entry))) ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point. ;; Use the right file names for KERNEL and INITRD in case ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a ;; separate partition. - (let ((kernel (strip-mount-point device-mount-point kernel)) - (initrd (strip-mount-point device-mount-point initrd))) + + ;; Also, in case a subvolume name could be extracted from the "subvol" + ;; option given to the "rootflags" argument of the kernel, it is + ;; prepended to the kernel and initrd paths, to allow booting from + ;; a Btrfs subvolume. + (let ((kernel (prepend-subvol subvol (strip-mount-point + device-mount-point kernel))) + (initrd (prepend-subvol subvol (strip-mount-point + device-mount-point initrd)))) #~(format port "menuentry ~s { ~a linux ~a ~a diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index f273957d78..7a30ebcef1 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -92,9 +92,12 @@ (define (find-long-option option arguments) "Find OPTION among ARGUMENTS, where OPTION is something like \"--load\". -Return the value associated with OPTION, or #f on failure." +Return the value associated with OPTION, or #f on failure. Any non-string +arguments are ignored." (let ((opt (string-append option "="))) - (and=> (find (cut string-prefix? opt <>) + (and=> (find (lambda (arg) + (and (string? arg) + (string-prefix? opt arg))) arguments) (lambda (arg) (substring arg (+ 1 (string-index arg #\=))))))) diff --git a/gnu/build/linux-modules.scm b/gnu/build/linux-modules.scm index a149eff329..5f2efe7484 100644 --- a/gnu/build/linux-modules.scm +++ b/gnu/build/linux-modules.scm @@ -32,7 +32,8 @@ #:use-module (ice-9 match) #:use-module (ice-9 rdelim) #:autoload (ice-9 pretty-print) (pretty-print) - #:export (dot-ko + #:export (%not-comma + dot-ko ensure-dot-ko module-formal-name module-aliases diff --git a/tests/grub.scm b/tests/grub.scm new file mode 100644 index 0000000000..81453b00ab --- /dev/null +++ b/tests/grub.scm @@ -0,0 +1,51 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2019 Maxim Cournoyer +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (test-grub) + #:use-module (gnu bootloader grub) + #:use-module (srfi srfi-64)) + +;;; Local bindings to private procedures that are to be tested. +(define arguments->subvol (@@ (gnu bootloader grub) arguments->subvol)) + + +(test-begin "grub") + +(test-equal "Get subvolume name from arguments, multiple options" + "@" + (arguments->subvol '("ro" "debug" + "rootflags=foo=bar,subvol=@,bar=baz"))) + +(test-equal "No subvolume from arguments" + #f + (arguments->subvol '("rootflags=foo=bar"))) + +(test-equal "No rootflags argument" + #f + (arguments->subvol '("ro" "debug"))) + +(test-equal "Empty arguments list" + #f + (arguments->subvol '())) + +;;; The other types would typically be gexp objects. +(test-equal "Argument list may contain other types than string" + "rootfs" + (arguments->subvol '(#f "rootflags=subvol=rootfs"))) + +(test-end "grub") -- 2.23.0