Remove traces of "GuixSD".
[jackhill/guix/guix.git] / gnu / system / vm.scm
index 594ba66..e561285 100644 (file)
@@ -1,9 +1,10 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
 ;;; Copyright © 2016, 2017 Leo Famulari <leo@famulari.name>
 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
 ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
+;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
   #:use-module (guix records)
   #:use-module (guix modules)
   #:use-module (guix utils)
-  #:use-module (guix hash)
+  #:use-module (gcrypt hash)
   #:use-module (guix base32)
+  #:use-module ((guix self) #:select (make-config.scm))
 
   #:use-module ((gnu build vm)
                 #:select (qemu-command))
   #:use-module (gnu packages base)
   #:use-module (gnu packages bootloaders)
   #:use-module (gnu packages cdrom)
+  #:use-module (gnu packages compression)
   #:use-module (gnu packages guile)
+  #:autoload   (gnu packages gnupg) (guile-gcrypt)
   #:use-module (gnu packages gawk)
   #:use-module (gnu packages bash)
   #:use-module (gnu packages less)
@@ -46,7 +50,6 @@
   #:use-module (gnu packages disk)
   #:use-module (gnu packages zile)
   #:use-module (gnu packages linux)
-  #:use-module (gnu packages package-management)
   #:use-module ((gnu packages make-bootstrap)
                 #:select (%guile-static-stripped))
   #:use-module (gnu packages admin)
@@ -75,6 +78,7 @@
             system-qemu-image/shared-store
             system-qemu-image/shared-store-script
             system-disk-image
+            system-docker-image
 
             virtual-machine
             virtual-machine?))
 ;;; Code:
 
 (define %linux-vm-file-systems
-  ;; File systems mounted for 'derivation-in-linux-vm'.  The store and /xchg
-  ;; directory are shared with the host over 9p.
+  ;; File systems mounted for 'derivation-in-linux-vm'.  These are shared with
+  ;; the host over 9p.
   (list (file-system
           (mount-point (%store-prefix))
           (device "store")
           (type "9p")
           (needed-for-boot? #t)
-          (options "trans=virtio")
+          (flags '(read-only))
+          (options "trans=virtio,cache=loose")
           (check? #f))
+
+        ;; The 9p documentation says that cache=loose is "intended for
+        ;; exclusive, read-only mounts", without additional details.  In
+        ;; practice it seems to work well for these, and it's much faster than
+        ;; the default cache=none, especially when copying and registering
+        ;; store items.
         (file-system
           (mount-point "/xchg")
           (device "xchg")
           (type "9p")
           (needed-for-boot? #t)
-          (options "trans=virtio")
+          (options "trans=virtio,cache=loose")
+          (check? #f))
+        (file-system
+          (mount-point "/tmp")
+          (device "tmp")
+          (type "9p")
+          (needed-for-boot? #t)
+          (options "trans=virtio,cache=loose")
           (check? #f))))
 
+(define not-config?
+  ;; Select (guix …) and (gnu …) modules, except (guix config).
+  (match-lambda
+    (('guix 'config) #f)
+    (('guix rest ...) #t)
+    (('gnu rest ...) #t)
+    (rest #f)))
+
+(define gcrypt-sqlite3&co
+  ;; Guile-Gcrypt, Guile-SQLite3, and their propagated inputs.
+  (append-map (lambda (package)
+                (cons package
+                      (match (package-transitive-propagated-inputs package)
+                        (((labels packages) ...)
+                         packages))))
+              (list guile-gcrypt guile-sqlite3)))
+
 (define* (expression->derivation-in-linux-vm name exp
                                              #:key
                                              (system (%current-system))
                                              (env-vars '())
                                              (guile-for-build
                                               (%guile-for-build))
+                                             (file-systems
+                                              %linux-vm-file-systems)
 
                                              (single-file-output? #f)
                                              (make-disk-image? #f)
                                              (disk-image-size 'guess))
   "Evaluate EXP in a QEMU virtual machine running LINUX with INITRD (a
 derivation).  The virtual machine runs with MEMORY-SIZE MiB of memory.  In the
-virtual machine, EXP has access to all its inputs from the store; it should
-put its output file(s) in the '/xchg' directory.
+virtual machine, EXP has access to FILE-SYSTEMS, which, by default, includes a
+9p share of the store, the '/xchg' where EXP should put its output file(s),
+and a 9p share of /tmp.
 
 If SINGLE-FILE-OUTPUT? is true, copy a single file from '/xchg' to OUTPUT.
 Otherwise, copy the contents of /xchg to a new directory OUTPUT.
@@ -136,55 +174,71 @@ based on the size of the closure of REFERENCES-GRAPHS.
 When REFERENCES-GRAPHS is true, it must be a list of file name/store path
 pairs, as for `derivation'.  The files containing the reference graphs are
 made available under the /xchg CIFS share."
-  (mlet* %store-monad
-      ((user-builder (gexp->file "builder-in-linux-vm" exp))
-       (loader       (gexp->file "linux-vm-loader"
-                                 #~(primitive-load #$user-builder)))
-       (coreutils -> (canonical-package coreutils))
-       (initrd       (if initrd                   ; use the default initrd?
-                         (return initrd)
-                         (base-initrd %linux-vm-file-systems
-                                      #:on-error 'backtrace
-                                      #:linux linux
-                                      #:linux-modules %base-initrd-modules
-                                      #:qemu-networking? #t))))
+  (define user-builder
+    (program-file "builder-in-linux-vm" exp))
+
+  (define loader
+    ;; Invoke USER-BUILDER instead using 'primitive-load'.  The reason for
+    ;; this is to allow USER-BUILDER to dlopen stuff by using a full-featured
+    ;; Guile, which it couldn't do using the statically-linked guile used in
+    ;; the initrd.  See example at
+    ;; <https://lists.gnu.org/archive/html/guix-devel/2017-10/msg00233.html>.
+    (program-file "linux-vm-loader"
+                  ;; When USER-BUILDER succeeds, reboot (indicating a
+                  ;; success), otherwise die, which causes a kernel panic
+                  ;; ("Attempted to kill init!").
+                  #~(when (zero? (system* #$user-builder))
+                      (reboot))))
+
+  (let ((initrd (or initrd
+                    (base-initrd file-systems
+                                 #:on-error 'backtrace
+                                 #:linux linux
+                                 #:linux-modules %base-initrd-modules
+                                 #:qemu-networking? #t))))
 
     (define builder
       ;; Code that launches the VM that evaluates EXP.
-      (with-imported-modules (source-module-closure '((guix build utils)
-                                                      (gnu build vm)))
-        #~(begin
-            (use-modules (guix build utils)
-                         (gnu build vm))
-
-            (let* ((inputs  '#$(list qemu coreutils))
-                   (linux   (string-append #$linux "/"
-                                           #$(system-linux-image-file-name)))
-                   (initrd  (string-append #$initrd "/initrd"))
-                   (loader  #$loader)
-                   (graphs  '#$(match references-graphs
-                                 (((graph-files . _) ...) graph-files)
-                                 (_ #f)))
-                   (size    #$(if (eq? 'guess disk-image-size)
-                                  #~(+ (* 70 (expt 2 20)) ;ESP
-                                       (estimated-partition-size graphs))
-                                  disk-image-size)))
-
-              (set-path-environment-variable "PATH" '("bin") inputs)
-
-              (load-in-linux-vm loader
-                                #:output #$output
-                                #:linux linux #:initrd initrd
-                                #:memory-size #$memory-size
-                                #:make-disk-image? #$make-disk-image?
-                                #:single-file-output? #$single-file-output?
-                                ;; FIXME: ‘target-arm32?’ may not operate on
-                                ;; the right system/target values.  Rewrite
-                                ;; using ‘let-system’ when available.
-                                #:target-arm32? #$(target-arm32?)
-                                #:disk-image-format #$disk-image-format
-                                #:disk-image-size size
-                                #:references-graphs graphs)))))
+      (with-extensions gcrypt-sqlite3&co
+        (with-imported-modules `(,@(source-module-closure
+                                    '((guix build utils)
+                                      (gnu build vm))
+                                    #:select? not-config?)
+
+                                 ;; For consumption by (gnu store database).
+                                 ((guix config) => ,(make-config.scm)))
+          #~(begin
+              (use-modules (guix build utils)
+                           (gnu build vm))
+
+              (let* ((inputs  '#$(list qemu (canonical-package coreutils)))
+                     (linux   (string-append #$linux "/"
+                                             #$(system-linux-image-file-name)))
+                     (initrd  #$initrd)
+                     (loader  #$loader)
+                     (graphs  '#$(match references-graphs
+                                   (((graph-files . _) ...) graph-files)
+                                   (_ #f)))
+                     (size    #$(if (eq? 'guess disk-image-size)
+                                    #~(+ (* 70 (expt 2 20)) ;ESP
+                                         (estimated-partition-size graphs))
+                                    disk-image-size)))
+
+                (set-path-environment-variable "PATH" '("bin") inputs)
+
+                (load-in-linux-vm loader
+                                  #:output #$output
+                                  #:linux linux #:initrd initrd
+                                  #:memory-size #$memory-size
+                                  #:make-disk-image? #$make-disk-image?
+                                  #:single-file-output? #$single-file-output?
+                                  ;; FIXME: ‘target-arm32?’ may not operate on
+                                  ;; the right system/target values.  Rewrite
+                                  ;; using ‘let-system’ when available.
+                                  #:target-arm32? #$(target-arm32?)
+                                  #:disk-image-format #$disk-image-format
+                                  #:disk-image-size size
+                                  #:references-graphs graphs))))))
 
     (gexp->derivation name builder
                       ;; TODO: Require the "kvm" feature.
@@ -199,7 +253,7 @@ made available under the /xchg CIFS share."
                         file-system-uuid
                         (system (%current-system))
                         (qemu qemu-minimal)
-                        os-drv
+                        os
                         bootcfg-drv
                         bootloader
                         register-closures?
@@ -207,44 +261,62 @@ made available under the /xchg CIFS share."
   "Return a bootable, stand-alone iso9660 image.
 
 INPUTS is a list of inputs (as for packages)."
+  (define schema
+    (and register-closures?
+         (local-file (search-path %load-path
+                                  "guix/store/schema.sql"))))
+
   (expression->derivation-in-linux-vm
    name
-   (with-imported-modules (source-module-closure '((gnu build vm)
-                                                   (guix build utils)))
-     #~(begin
-         (use-modules (gnu build vm)
-                      (guix build utils))
-
-         (let ((inputs
-                '#$(append (list qemu parted e2fsprogs dosfstools xorriso)
-                           (map canonical-package
-                                (list sed grep coreutils findutils gawk))
-                           (if register-closures? (list guix) '())))
-
-
-               (graphs     '#$(match inputs
-                                   (((names . _) ...)
-                                    names)))
-               ;; This variable is unused but allows us to add INPUTS-TO-COPY
-               ;; as inputs.
-               (to-register
-                '#$(map (match-lambda
-                          ((name thing) thing)
-                          ((name thing output) `(,thing ,output)))
-                        inputs)))
-
-           (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
-           (make-iso9660-image #$(bootloader-package bootloader)
-                               #$bootcfg-drv
-                               #$os-drv
-                               "/xchg/guixsd.iso"
-                               #:register-closures? #$register-closures?
-                               #:closures graphs
-                               #:volume-id #$file-system-label
-                               #:volume-uuid #$(and=> file-system-uuid
-                                                      uuid-bytevector))
-           (reboot))))
+   (with-extensions gcrypt-sqlite3&co
+     (with-imported-modules `(,@(source-module-closure '((gnu build vm)
+                                                         (guix store database)
+                                                         (guix build utils))
+                                                       #:select? not-config?)
+                              ((guix config) => ,(make-config.scm)))
+       #~(begin
+           (use-modules (gnu build vm)
+                        (guix store database)
+                        (guix build utils))
+
+           (sql-schema #$schema)
+
+           (let ((inputs
+                  '#$(append (list qemu parted e2fsprogs dosfstools xorriso)
+                             (map canonical-package
+                                  (list sed grep coreutils findutils gawk))))
+
+
+                 (graphs     '#$(match inputs
+                                  (((names . _) ...)
+                                   names)))
+                 ;; This variable is unused but allows us to add INPUTS-TO-COPY
+                 ;; as inputs.
+                 (to-register
+                  '#$(map (match-lambda
+                            ((name thing) thing)
+                            ((name thing output) `(,thing ,output)))
+                          inputs)))
+
+             (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
+             (make-iso9660-image #$(bootloader-package bootloader)
+                                 #$bootcfg-drv
+                                 #$os
+                                 "/xchg/guixsd.iso"
+                                 #:register-closures? #$register-closures?
+                                 #:closures graphs
+                                 #:volume-id #$file-system-label
+                                 #:volume-uuid #$(and=> file-system-uuid
+                                                        uuid-bytevector))))))
    #:system system
+
+   ;; Keep a local file system for /tmp so that we can populate it directly as
+   ;; root and have files owned by root.  See <https://bugs.gnu.org/31752>.
+   #:file-systems (remove (lambda (file-system)
+                            (string=? (file-system-mount-point file-system)
+                                      "/tmp"))
+                          %linux-vm-file-systems)
+
    #:make-disk-image? #f
    #:single-file-output? #t
    #:references-graphs inputs))
@@ -258,7 +330,7 @@ INPUTS is a list of inputs (as for packages)."
                      (file-system-type "ext4")
                      file-system-label
                      file-system-uuid
-                     os-drv
+                     os
                      bootcfg-drv
                      bootloader
                      (register-closures? #t)
@@ -278,97 +350,188 @@ INPUTS is a list of inputs (as for packages).  When COPY-INPUTS? is true, copy
 all of INPUTS into the image being built.  When REGISTER-CLOSURES? is true,
 register INPUTS in the store database of the image so that Guix can be used in
 the image."
+  (define schema
+    (and register-closures?
+         (local-file (search-path %load-path
+                                  "guix/store/schema.sql"))))
+
   (expression->derivation-in-linux-vm
    name
-   (with-imported-modules (source-module-closure '((gnu build bootloader)
-                                                   (gnu build vm)
-                                                   (guix build utils)))
-     #~(begin
-         (use-modules (gnu build bootloader)
-                      (gnu build vm)
-                      (guix build utils)
-                      (srfi srfi-26)
-                      (ice-9 binary-ports))
-
-         (let ((inputs
-                '#$(append (list qemu parted e2fsprogs dosfstools)
-                           (map canonical-package
-                                (list sed grep coreutils findutils gawk))
-                           (if register-closures? (list guix) '())))
-
-               ;; This variable is unused but allows us to add INPUTS-TO-COPY
-               ;; as inputs.
-               (to-register
-                '#$(map (match-lambda
-                          ((name thing) thing)
-                          ((name thing output) `(,thing ,output)))
-                        inputs)))
-
-           (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
-
-           (let* ((graphs     '#$(match inputs
-                                   (((names . _) ...)
-                                    names)))
-                  (initialize (root-partition-initializer
-                               #:closures graphs
-                               #:copy-closures? #$copy-inputs?
-                               #:register-closures? #$register-closures?
-                               #:system-directory #$os-drv))
-                  (root-size  #$(if (eq? 'guess disk-image-size)
-                                    #~(max
-                                       ;; Minimum 20 MiB root size
-                                       (* 20 (expt 2 20))
-                                       (estimated-partition-size
-                                        (map (cut string-append "/xchg/" <>)
-                                             graphs)))
-                                    (- disk-image-size
-                                       (* 50 (expt 2 20)))))
-                  (partitions
-                   (append
-                    (list (partition
-                           (size root-size)
-                           (label #$file-system-label)
-                           (uuid #$(and=> file-system-uuid
-                                          uuid-bytevector))
-                           (file-system #$file-system-type)
-                           (flags '(boot))
-                           (initializer initialize)))
-                    ;; Append a small EFI System Partition for use with UEFI
-                    ;; bootloaders if we are not targeting ARM because UEFI
-                    ;; support in U-Boot is experimental.
-                    ;;
-                    ;; FIXME: ‘target-arm32?’ may be not operate on the right
-                    ;; system/target values.  Rewrite using ‘let-system’ when
-                    ;; available.
-                    (if #$(target-arm32?)
-                        '()
-                        (list (partition
-                               ;; The standalone grub image is about 10MiB, but
-                               ;; leave some room for custom or multiple images.
-                               (size (* 40 (expt 2 20)))
-                               (label "GNU-ESP")             ;cosmetic only
-                               ;; Use "vfat" here since this property is used
-                               ;; when mounting. The actual FAT-ness is based
-                               ;; on file system size (16 in this case).
-                               (file-system "vfat")
-                               (flags '(esp))))))))
-             (initialize-hard-disk "/dev/vda"
-                                   #:partitions partitions
-                                   #:grub-efi #$grub-efi
-                                   #:bootloader-package
-                                   #$(bootloader-package bootloader)
-                                   #:bootcfg #$bootcfg-drv
-                                   #:bootcfg-location
-                                   #$(bootloader-configuration-file bootloader)
-                                   #:bootloader-installer
-                                   #$(bootloader-installer bootloader))
-             (reboot)))))
+   (with-extensions gcrypt-sqlite3&co
+     (with-imported-modules `(,@(source-module-closure '((gnu build vm)
+                                                         (gnu build bootloader)
+                                                         (guix store database)
+                                                         (guix build utils))
+                                                       #:select? not-config?)
+                              ((guix config) => ,(make-config.scm)))
+       #~(begin
+           (use-modules (gnu build bootloader)
+                        (gnu build vm)
+                        (guix store database)
+                        (guix build utils)
+                        (srfi srfi-26)
+                        (ice-9 binary-ports))
+
+           (sql-schema #$schema)
+
+           (let ((inputs
+                  '#$(append (list qemu parted e2fsprogs dosfstools)
+                             (map canonical-package
+                                  (list sed grep coreutils findutils gawk))))
+
+                 ;; This variable is unused but allows us to add INPUTS-TO-COPY
+                 ;; as inputs.
+                 (to-register
+                  '#$(map (match-lambda
+                            ((name thing) thing)
+                            ((name thing output) `(,thing ,output)))
+                          inputs)))
+
+             (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
+
+             (let* ((graphs     '#$(match inputs
+                                     (((names . _) ...)
+                                      names)))
+                    (initialize (root-partition-initializer
+                                 #:closures graphs
+                                 #:copy-closures? #$copy-inputs?
+                                 #:register-closures? #$register-closures?
+                                 #:system-directory #$os
+
+                                 ;; Disable deduplication to speed things up,
+                                 ;; and because it doesn't help much for a
+                                 ;; single system generation.
+                                 #:deduplicate? #f))
+                    (root-size  #$(if (eq? 'guess disk-image-size)
+                                      #~(max
+                                         ;; Minimum 20 MiB root size
+                                         (* 20 (expt 2 20))
+                                         (estimated-partition-size
+                                          (map (cut string-append "/xchg/" <>)
+                                               graphs)))
+                                      (- disk-image-size
+                                         (* 50 (expt 2 20)))))
+                    (partitions
+                     (append
+                      (list (partition
+                             (size root-size)
+                             (label #$file-system-label)
+                             (uuid #$(and=> file-system-uuid
+                                            uuid-bytevector))
+                             (file-system #$file-system-type)
+                             (flags '(boot))
+                             (initializer initialize)))
+                      ;; Append a small EFI System Partition for use with UEFI
+                      ;; bootloaders if we are not targeting ARM because UEFI
+                      ;; support in U-Boot is experimental.
+                      ;;
+                      ;; FIXME: ‘target-arm32?’ may be not operate on the right
+                      ;; system/target values.  Rewrite using ‘let-system’ when
+                      ;; available.
+                      (if #$(target-arm32?)
+                          '()
+                          (list (partition
+                                 ;; The standalone grub image is about 10MiB, but
+                                 ;; leave some room for custom or multiple images.
+                                 (size (* 40 (expt 2 20)))
+                                 (label "GNU-ESP") ;cosmetic only
+                                 ;; Use "vfat" here since this property is used
+                                 ;; when mounting. The actual FAT-ness is based
+                                 ;; on file system size (16 in this case).
+                                 (file-system "vfat")
+                                 (flags '(esp))))))))
+               (initialize-hard-disk "/dev/vda"
+                                     #:partitions partitions
+                                     #:grub-efi #$grub-efi
+                                     #:bootloader-package
+                                     #$(bootloader-package bootloader)
+                                     #:bootcfg #$bootcfg-drv
+                                     #:bootcfg-location
+                                     #$(bootloader-configuration-file bootloader)
+                                     #:bootloader-installer
+                                     #$(bootloader-installer bootloader)))))))
    #:system system
    #:make-disk-image? #t
    #:disk-image-size disk-image-size
    #:disk-image-format disk-image-format
    #:references-graphs inputs))
 
+(define* (system-docker-image os
+                              #:key
+                              (name "guixsd-docker-image")
+                              register-closures?)
+  "Build a docker image.  OS is the desired <operating-system>.  NAME is the
+base name to use for the output file.  When REGISTER-CLOSURES? is not #f,
+register the closure of OS with Guix in the resulting Docker image.  This only
+makes sense when you want to build a Guix System Docker image that has Guix
+installed inside of it.  If you don't need Guix (e.g., your Docker
+image just contains a web server that is started by the Shepherd), then you
+should set REGISTER-CLOSURES? to #f."
+  (define schema
+    (and register-closures?
+         (local-file (search-path %load-path
+                                  "guix/store/schema.sql"))))
+
+  (mlet %store-monad ((os-drv (operating-system-derivation os #:container? #t))
+                      (name -> (string-append name ".tar.gz"))
+                      (graph -> "system-graph"))
+    (define build
+      (with-extensions (cons guile-json           ;for (guix docker)
+                             gcrypt-sqlite3&co)   ;for (guix store database)
+        (with-imported-modules `(,@(source-module-closure
+                                    '((guix docker)
+                                      (guix store database)
+                                      (guix build utils)
+                                      (guix build store-copy)
+                                      (gnu build vm))
+                                    #:select? not-config?)
+                                 ((guix config) => ,(make-config.scm)))
+          #~(begin
+              (use-modules (guix docker)
+                           (guix build utils)
+                           (gnu build vm)
+                           (srfi srfi-19)
+                           (guix build store-copy)
+                           (guix store database))
+
+              ;; Set the SQL schema location.
+              (sql-schema #$schema)
+
+              (let* (;; This initializer requires elevated privileges that are
+                     ;; not normally available in the build environment (e.g.,
+                     ;; it needs to create device nodes).  In order to obtain
+                     ;; such privileges, we run it as root in a VM.
+                     (initialize (root-partition-initializer
+                                  #:closures '(#$graph)
+                                  #:register-closures? #$register-closures?
+                                  #:system-directory #$os-drv
+                                  ;; De-duplication would fail due to
+                                  ;; cross-device link errors, so don't do it.
+                                  #:deduplicate? #f))
+                     ;; Even as root in a VM, the initializer would fail due to
+                     ;; lack of privileges if we use a root-directory that is on
+                     ;; a file system that is shared with the host (e.g., /tmp).
+                     (root-directory "/guixsd-system-root"))
+                (set-path-environment-variable "PATH" '("bin" "sbin") '(#+tar))
+                (mkdir root-directory)
+                (initialize root-directory)
+                (build-docker-image
+                 (string-append "/xchg/" #$name) ;; The output file.
+                 (cons* root-directory
+                        (map store-info-item
+                             (call-with-input-file
+                                 (string-append "/xchg/" #$graph)
+                               read-reference-graph)))
+                 #$os-drv
+                 #:compressor '(#+(file-append gzip "/bin/gzip") "-9n")
+                 #:creation-time (make-time time-utc 0 1)
+                 #:transformations `((,root-directory -> ""))))))))
+    (expression->derivation-in-linux-vm
+     name build
+     #:make-disk-image? #f
+     #:single-file-output? #t
+     #:references-graphs `((,graph ,os-drv)))))
+
 \f
 ;;;
 ;;; VM and disk images.
@@ -377,17 +540,42 @@ the image."
 (define* (operating-system-uuid os #:optional (type 'dce))
   "Compute UUID object with a deterministic \"UUID\" for OS, of the given
 TYPE (one of 'iso9660 or 'dce).  Return a UUID object."
+  ;; Note: For this to be deterministic, we must not hash things that contains
+  ;; (directly or indirectly) procedures, for example.  That rules out
+  ;; anything that contains gexps, thunk or delayed record fields, etc.
+
+  (define service-name
+    (compose service-type-name service-kind))
+
+  (define (file-system-digest fs)
+    ;; Return a hashable digest that does not contain 'dependencies' since
+    ;; this field can contain procedures.
+    (let ((device (file-system-device fs)))
+      (list (file-system-mount-point fs)
+            (file-system-type fs)
+            (cond ((file-system-label? device)
+                   (file-system-label->string device))
+                  ((uuid? device)
+                   (uuid->string device))
+                  ((string? device)
+                   device)
+                  (else #f))
+            (file-system-options fs))))
+
   (if (eq? type 'iso9660)
       (let ((pad (compose (cut string-pad <> 2 #\0)
                           number->string))
-            (h   (hash (operating-system-services os) 3600)))
+            (h   (hash (map service-name (operating-system-services os))
+                       3600)))
         (bytevector->uuid
          (string->iso9660-uuid
           (string-append "1970-01-01-"
                          (pad (hash (operating-system-host-name os) 24)) "-"
                          (pad (quotient h 60)) "-"
                          (pad (modulo h 60)) "-"
-                         (pad (hash (operating-system-file-systems os) 100))))
+                         (pad (hash (map file-system-digest
+                                         (operating-system-file-systems os))
+                                    100))))
          'iso9660))
       (bytevector->uuid
        (uint-list->bytevector
@@ -395,9 +583,9 @@ TYPE (one of 'iso9660 or 'dce).  Return a UUID object."
                     (- (expt 2 32) 1))
               (hash (operating-system-host-name os)
                     (- (expt 2 32) 1))
-              (hash (operating-system-services os)
+              (hash (map service-name (operating-system-services os))
                     (- (expt 2 32) 1))
-              (hash (operating-system-file-systems os)
+              (hash (map file-system-digest (operating-system-file-systems os))
                     (- (expt 2 32) 1)))
         (endianness little)
         4)
@@ -422,7 +610,7 @@ to USB sticks meant to be read-only."
 
   (define root-label
     ;; Volume name of the root file system.
-    (normalize-label "GuixSD_image"))
+    (normalize-label "Guix_image"))
 
   (define root-uuid
     ;; UUID of the root file system, computed in a deterministic fashion.
@@ -438,57 +626,54 @@ to USB sticks meant to be read-only."
               (string=? (file-system-mount-point fs) "/"))
             (operating-system-file-systems os)))
 
-  (let ((os (operating-system (inherit os)
-              ;; Since this is meant to be used on real hardware, don't
-              ;; install QEMU networking or anything like that.  Assume USB
-              ;; mass storage devices (usb-storage.ko) are available.
-              (initrd (lambda (file-systems . rest)
-                        (apply (operating-system-initrd os)
-                               file-systems
-                               #:volatile-root? #t
-                               rest)))
-
-              (bootloader (if (string=? "iso9660" file-system-type)
-                              (bootloader-configuration
-                                (inherit (operating-system-bootloader os))
-                                (bootloader grub-mkrescue-bootloader))
-                              (operating-system-bootloader os)))
-
-              ;; Force our own root file system.
-              (file-systems (cons (file-system
-                                    (mount-point "/")
-                                    (device root-uuid)
-                                    (title 'uuid)
-                                    (type file-system-type))
-                                  file-systems-to-keep)))))
-
-    (mlet* %store-monad ((os-drv   (operating-system-derivation os))
-                         (bootcfg  (operating-system-bootcfg os)))
-      (if (string=? "iso9660" file-system-type)
-          (iso9660-image #:name name
-                         #:file-system-label root-label
-                         #:file-system-uuid root-uuid
-                         #:os-drv os-drv
-                         #:register-closures? #t
-                         #:bootcfg-drv bootcfg
-                         #:bootloader (bootloader-configuration-bootloader
-                                        (operating-system-bootloader os))
-                         #:inputs `(("system" ,os-drv)
-                                    ("bootcfg" ,bootcfg)))
-          (qemu-image #:name name
-                      #:os-drv os-drv
-                      #:bootcfg-drv bootcfg
-                      #:bootloader (bootloader-configuration-bootloader
-                                    (operating-system-bootloader os))
-                      #:disk-image-size disk-image-size
-                      #:disk-image-format "raw"
-                      #:file-system-type file-system-type
-                      #:file-system-label root-label
-                      #:file-system-uuid root-uuid
-                      #:copy-inputs? #t
-                      #:register-closures? #t
-                      #:inputs `(("system" ,os-drv)
-                                 ("bootcfg" ,bootcfg)))))))
+  (let* ((os (operating-system (inherit os)
+               ;; Since this is meant to be used on real hardware, don't
+               ;; install QEMU networking or anything like that.  Assume USB
+               ;; mass storage devices (usb-storage.ko) are available.
+               (initrd (lambda (file-systems . rest)
+                         (apply (operating-system-initrd os)
+                                file-systems
+                                #:volatile-root? #t
+                                rest)))
+
+               (bootloader (if (string=? "iso9660" file-system-type)
+                               (bootloader-configuration
+                                 (inherit (operating-system-bootloader os))
+                                 (bootloader grub-mkrescue-bootloader))
+                               (operating-system-bootloader os)))
+
+               ;; Force our own root file system.
+               (file-systems (cons (file-system
+                                     (mount-point "/")
+                                     (device root-uuid)
+                                     (type file-system-type))
+                                   file-systems-to-keep))))
+        (bootcfg (operating-system-bootcfg os)))
+    (if (string=? "iso9660" file-system-type)
+        (iso9660-image #:name name
+                       #:file-system-label root-label
+                       #:file-system-uuid root-uuid
+                       #:os os
+                       #:register-closures? #t
+                       #:bootcfg-drv bootcfg
+                       #:bootloader (bootloader-configuration-bootloader
+                                     (operating-system-bootloader os))
+                       #:inputs `(("system" ,os)
+                                  ("bootcfg" ,bootcfg)))
+        (qemu-image #:name name
+                    #:os os
+                    #:bootcfg-drv bootcfg
+                    #:bootloader (bootloader-configuration-bootloader
+                                  (operating-system-bootloader os))
+                    #:disk-image-size disk-image-size
+                    #:disk-image-format "raw"
+                    #:file-system-type file-system-type
+                    #:file-system-label root-label
+                    #:file-system-uuid root-uuid
+                    #:copy-inputs? #t
+                    #:register-closures? #t
+                    #:inputs `(("system" ,os)
+                               ("bootcfg" ,bootcfg))))))
 
 (define* (system-qemu-image os
                             #:key
@@ -514,31 +699,28 @@ of the GNU system as described by OS."
                                'dce)))
 
 
-  (let ((os (operating-system (inherit os)
-              ;; Assume we have an initrd with the whole QEMU shebang.
-
-              ;; Force our own root file system.  Refer to it by UUID so that
-              ;; it works regardless of how the image is used ("qemu -hda",
-              ;; Xen, etc.).
-              (file-systems (cons (file-system
-                                    (mount-point "/")
-                                    (device root-uuid)
-                                    (title 'uuid)
-                                    (type file-system-type))
-                                  file-systems-to-keep)))))
-    (mlet* %store-monad
-        ((os-drv      (operating-system-derivation os))
-         (bootcfg     (operating-system-bootcfg os)))
-      (qemu-image  #:os-drv os-drv
-                   #:bootcfg-drv bootcfg
-                   #:bootloader (bootloader-configuration-bootloader
-                                 (operating-system-bootloader os))
-                   #:disk-image-size disk-image-size
-                   #:file-system-type file-system-type
-                   #:file-system-uuid root-uuid
-                   #:inputs `(("system" ,os-drv)
-                              ("bootcfg" ,bootcfg))
-                   #:copy-inputs? #t))))
+  (let* ((os (operating-system (inherit os)
+               ;; Assume we have an initrd with the whole QEMU shebang.
+
+               ;; Force our own root file system.  Refer to it by UUID so that
+               ;; it works regardless of how the image is used ("qemu -hda",
+               ;; Xen, etc.).
+               (file-systems (cons (file-system
+                                     (mount-point "/")
+                                     (device root-uuid)
+                                     (type file-system-type))
+                                   file-systems-to-keep))))
+         (bootcfg (operating-system-bootcfg os)))
+    (qemu-image  #:os os
+                 #:bootcfg-drv bootcfg
+                 #:bootloader (bootloader-configuration-bootloader
+                               (operating-system-bootloader os))
+                 #:disk-image-size disk-image-size
+                 #:file-system-type file-system-type
+                 #:file-system-uuid root-uuid
+                 #:inputs `(("system" ,os)
+                            ("bootcfg" ,bootcfg))
+                 #:copy-inputs? #t)))
 
 \f
 ;;;
@@ -580,13 +762,12 @@ environment with the store shared with the host.  MAPPINGS is a list of
                     (source (file-system-device fs)))
                 (or (string=? target (%store-prefix))
                     (string=? target "/")
-                    (and (eq? 'device (file-system-title fs))
+                    (and (string? source)
                          (string-prefix? "/dev/" source))
 
                     ;; Labels and UUIDs are necessarily invalid in the VM.
                     (and (file-system-mount? fs)
-                         (or (eq? 'label (file-system-title fs))
-                             (eq? 'uuid (file-system-title fs))
+                         (or (file-system-label? source)
                              (uuid? source))))))
             (operating-system-file-systems os)))
 
@@ -639,24 +820,30 @@ with the host.
 When FULL-BOOT? is true, return an image that does a complete boot sequence,
 bootloaded included; thus, make a disk image that contains everything the
 bootloader refers to: OS kernel, initrd, bootloader data, etc."
-  (mlet* %store-monad ((os-drv   (operating-system-derivation os))
-                       (bootcfg  (operating-system-bootcfg os)))
-    ;; XXX: When FULL-BOOT? is true, we end up creating an image that contains
-    ;; BOOTCFG and all its dependencies, including the output of OS-DRV.
-    ;; This is more than needed (we only need the kernel, initrd, GRUB for its
-    ;; font, and the background image), but it's hard to filter that.
-    (qemu-image #:os-drv os-drv
-                #:bootcfg-drv bootcfg
-                #:bootloader (bootloader-configuration-bootloader
-                              (operating-system-bootloader os))
-                #:disk-image-size disk-image-size
-                #:inputs (if full-boot?
-                             `(("bootcfg" ,bootcfg))
-                             '())
-
-                ;; XXX: Passing #t here is too slow, so let it off by default.
-                #:register-closures? #f
-                #:copy-inputs? full-boot?)))
+  (define root-uuid
+    ;; Use a fixed UUID to improve determinism.
+    (operating-system-uuid os 'dce))
+
+  (define bootcfg
+    (operating-system-bootcfg os))
+
+  ;; XXX: When FULL-BOOT? is true, we end up creating an image that contains
+  ;; BOOTCFG and all its dependencies, including the output of OS.
+  ;; This is more than needed (we only need the kernel, initrd, GRUB for its
+  ;; font, and the background image), but it's hard to filter that.
+  (qemu-image #:os os
+              #:bootcfg-drv bootcfg
+              #:bootloader (bootloader-configuration-bootloader
+                            (operating-system-bootloader os))
+              #:disk-image-size disk-image-size
+              #:file-system-uuid root-uuid
+              #:inputs (if full-boot?
+                           `(("bootcfg" ,bootcfg))
+                           '())
+
+              ;; XXX: Passing #t here is too slow, so let it off by default.
+              #:register-closures? #f
+              #:copy-inputs? full-boot?))
 
 (define* (common-qemu-options image shared-fs)
   "Return the a string-value gexp with the common QEMU options to boot IMAGE,
@@ -706,21 +893,20 @@ bootloader; otherwise it directly starts the operating system kernel.  The
 DISK-IMAGE-SIZE parameter specifies the size in bytes of the root disk image;
 it is mostly useful when FULL-BOOT?  is true."
   (mlet* %store-monad ((os ->  (virtualized-operating-system os mappings full-boot?))
-                       (os-drv (operating-system-derivation os))
                        (image  (system-qemu-image/shared-store
                                 os
                                 #:full-boot? full-boot?
                                 #:disk-image-size disk-image-size)))
     (define kernel-arguments
       #~(list #$@(if graphic? #~() #~("console=ttyS0"))
-              #+@(operating-system-kernel-arguments os os-drv "/dev/vda1")))
+              #+@(operating-system-kernel-arguments os "/dev/vda1")))
 
     (define qemu-exec
       #~(list (string-append #$qemu "/bin/" #$(qemu-command (%current-system)))
               #$@(if full-boot?
                      #~()
                      #~("-kernel" #$(operating-system-kernel-file os)
-                        "-initrd" #$(file-append os-drv "/initrd")
+                        "-initrd" #$(file-append os "/initrd")
                         (format #f "-append ~s"
                                 (string-join #$kernel-arguments " "))))
               #$@(common-qemu-options image