summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2018-09-07 09:50:26 +0200
committerLudovic Courtès <ludo@gnu.org>2018-09-07 11:40:05 +0200
commit1540075c790dfaeff52c93392f2fc63b9e23b77e (patch)
treedc9c011af6bd8a6cdde92f84e0eb5ed9da957c6e
parent0012e0dd5642fbbb8ee40a68f65afc184952fc98 (diff)
downloadguix-1540075c790dfaeff52c93392f2fc63b9e23b77e.tar.gz
vm: Make UUID computation really deterministic.
Fixes <https://bugs.gnu.org/32652>.

* gnu/system/vm.scm (operating-system-uuid)[service-name,
file-system-digest]: New procedures.
Map these over services and file systems and hash the result.
* tests/guix-system.sh: Add test.
-rw-r--r--gnu/system/vm.scm33
-rw-r--r--tests/guix-system.sh8
2 files changed, 37 insertions, 4 deletions
diff --git a/gnu/system/vm.scm b/gnu/system/vm.scm
index 3898872a46..91e117b9f3 100644
--- a/gnu/system/vm.scm
+++ b/gnu/system/vm.scm
@@ -529,17 +529,42 @@ should set REGISTER-CLOSURES? to #f."
 (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
@@ -547,9 +572,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)
diff --git a/tests/guix-system.sh b/tests/guix-system.sh
index 36ba5fbd5f..a129efdfcb 100644
--- a/tests/guix-system.sh
+++ b/tests/guix-system.sh
@@ -232,6 +232,14 @@ guix system build "$tmpfile" -d | grep '\.drv$'
 guix system vm "$tmpfile" -d	      # succeeds
 guix system vm "$tmpfile" -d | grep '\.drv$'
 
+# Make sure the behavior is deterministic (<https://bugs.gnu.org/32652>).
+drv1="`guix system vm "$tmpfile" -d`"
+drv2="`guix system vm "$tmpfile" -d`"
+test "$drv1" = "$drv2"
+drv1="`guix system disk-image --file-system-type=iso9660 "$tmpfile" -d`"
+drv2="`guix system disk-image --file-system-type=iso9660 "$tmpfile" -d`"
+test "$drv1" = "$drv2"
+
 make_user_config "group-that-does-not-exist" "users"
 if guix system build "$tmpfile" -n 2> "$errorfile"
 then false