Subject: [PATCH v3 11/14] gnu: build: bootloader: Add efi-bootnums procedure.

From: Herman Rimm <[email-masked]>
Date: Thu, 26 Sep 2024 12:09:08 +0200

From: Lilah Tascheter <[email-masked]>

* gnu/build/bootloader.scm (atomic-copy, efi-bootnums): Add procedures.
(in-temporary-directory): Add macro.

Change-Id: I3654d160f7306bb45a78b82ea6b249ff4281f739
---
 gnu/build/bootloader.scm | 48 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/gnu/build/bootloader.scm b/gnu/build/bootloader.scm
index af6063a884..3934e03aee 100644
--- a/gnu/build/bootloader.scm
+++ b/gnu/build/bootloader.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2019 Ludovic Courtès <[email-masked]>
 ;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
 ;;; Copyright © 2022 Timothy Sample <[email-masked]>
+;;; Copyright © 2024 Lilah Tascheter <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -20,13 +21,25 @@
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (gnu build bootloader)
+  #:autoload   (guix build syscalls) (free-disk-space)
   #:use-module (guix build utils)
   #:use-module (guix utils)
   #:use-module (ice-9 binary-ports)
+  #:use-module (guix diagnostics)
+  #:use-module (guix i18n)
   #:use-module (ice-9 format)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 popen)
+  #:use-module (ice-9 receive)
+  #:use-module (ice-9 regex)
   #:use-module (rnrs io ports)
   #:use-module (rnrs io simple)
-  #:export (write-file-on-device
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-35)
+  #:export (atomic-copy
+            in-temporary-directory
+            write-file-on-device
             install-efi-loader))
 
 
@@ -34,6 +47,21 @@ (define-module (gnu build bootloader)
 ;;; Writing utils.
 ;;;
 
+(define (atomic-copy from to)
+  (let ((pivot (string-append to ".new")))
+    (copy-file from pivot)
+    (rename-file pivot to)))
+
+(define-syntax-rule (in-temporary-directory blocks ...)
+  "Run BLOCKS while chdir'd into a temporary directory."
+  ;; Under POSIX.1-2008, mkdtemp must make the dir with 700 perms.
+  (let* ((tmp (or (getenv "TMPDIR") "/tmp"))
+         (dir (mkdtemp (string-append tmp "/guix-bootloader.XXXXXX")))
+         (cwd (getcwd)))
+    (dynamic-wind (lambda () (chdir dir))
+                  (lambda () blocks ...)
+                  (lambda () (chdir cwd) (delete-file-recursively dir)))))
+
 (define (write-file-on-device file size device offset)
   "Write SIZE bytes from FILE to DEVICE starting at OFFSET."
   (call-with-input-file file
@@ -56,6 +84,24 @@ (define (write-file-on-device file size device offset)
 ;;; EFI bootloader.
 ;;;
 
+;; XXX: Parsing efibootmgr output may be kinda jank.  A better way may exist.
+(define (efi-bootnums efibootmgr)
+  "Returns '(path . bootnum) pairs for each EFI boot entry.  bootnum is
+a string, and path is backslash-deliminated and relative to the ESP."
+  (let* ((pipe (open-pipe* OPEN_READ efibootmgr))
+         (text (get-string-all pipe))
+         (status (status:exit-val (close-pipe pipe)))
+         (bootnum-pattern
+           "^Boot([0-9a-fA-F]+).*[^A-Za-z]File\\(([^)]+)\\)$"))
+    (unless (zero? status)
+      (raise-exception
+        (formatted-message (G_ "efibootmgr exited with error code ~a") status)))
+    (fold-matches (make-regexp bootnum-pattern regexp/newline) text '()
+                  (lambda (match acc)
+                    (let* ((path (match:substring match 2))
+                           (bootnum (match:substring match 1)))
+                      (cons (cons path bootnum) acc))))))
+
 (define* (install-efi grub grub-config esp #:key targets)
   "Write a self-contained GRUB EFI loader to the mounted ESP using
 GRUB-CONFIG.
-- 
2.45.2