summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2013-12-20 17:17:42 +0100
committerLudovic Courtès <ludo@gnu.org>2013-12-20 17:17:42 +0100
commit526382ff92b20f6c651f03711c160c0c88264b88 (patch)
tree6fc13ecae4c6bccf635511acd7d38e26b29644ef
parentce507041f79bd66f54ce406d20b9e33a328a3f3d (diff)
daemon: Implement signed archive import/export.
* guix/scripts/authenticate.scm, nix/scripts/guix-authenticate.in, tests/signing-key.pub, tests/signing-key.sec: New files. * po/POTFILES.in: Add 'guix/scripts/authenticate.scm'. * guix/store.scm (dump-port): New procedure. (process-stderr): Add 'user-port' optional parameter. Handle the %STDERR-WRITE and %STDERR-READ cases as expected. (import-paths, export-path, export-paths): New procedures. * tests/store.scm ("export/import several paths", "import corrupt path"): New tests. * Makefile.am (MODULES): Add 'guix/scripts/authenticate.scm'. (EXTRA_DIST): Add 'tests/signing-key.{pub,sec}'. * daemon.am (libstore_a_CPPFLAGS)[-DNIX_CONF_DIR]: Change 'NIX_CONF_DIR' to .../guix. Change 'OPENSSL_PATH' to 'guix-authenticate'. * config-daemon.ac: Instantiate 'nix/scripts/guix-authenticate'. * nix/nix-daemon/guix-daemon.cc (main): Augment $PATH to include 'settings.nixLibexecDir'. * test-env.in: Export 'NIX_CONF_DIR' and 'NIX_LIBEXEC_DIR'. Populate $NIX_CONF_DIR.
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am3
-rw-r--r--config-daemon.ac2
-rw-r--r--daemon.am4
-rw-r--r--guix/scripts/authenticate.scm98
-rw-r--r--guix/store.scm79
-rw-r--r--nix/nix-daemon/guix-daemon.cc6
-rw-r--r--nix/scripts/guix-authenticate.in11
-rw-r--r--po/POTFILES.in1
-rw-r--r--test-env.in18
-rw-r--r--tests/signing-key.pub4
-rw-r--r--tests/signing-key.sec8
-rw-r--r--tests/store.scm45
13 files changed, 273 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index a8a5cad74c9..09a593e9fa0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -84,3 +84,4 @@ GPATH
84GRTAGS 84GRTAGS
85GTAGS 85GTAGS
86/nix-setuid-helper 86/nix-setuid-helper
87/nix/scripts/guix-authenticate
diff --git a/Makefile.am b/Makefile.am
index 2db77d57f3e..34846c3e29e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -73,6 +73,7 @@ MODULES = \
73 guix/scripts/hash.scm \ 73 guix/scripts/hash.scm \
74 guix/scripts/pull.scm \ 74 guix/scripts/pull.scm \
75 guix/scripts/substitute-binary.scm \ 75 guix/scripts/substitute-binary.scm \
76 guix/scripts/authenticate.scm \
76 guix/scripts/refresh.scm \ 77 guix/scripts/refresh.scm \
77 guix.scm \ 78 guix.scm \
78 $(GNU_SYSTEM_MODULES) 79 $(GNU_SYSTEM_MODULES)
@@ -172,6 +173,8 @@ EXTRA_DIST = \
172 srfi/srfi-64.scm \ 173 srfi/srfi-64.scm \
173 srfi/srfi-64.upstream.scm \ 174 srfi/srfi-64.upstream.scm \
174 tests/test.drv \ 175 tests/test.drv \
176 tests/signing-key.pub \
177 tests/signing-key.sec \
175 build-aux/config.rpath \ 178 build-aux/config.rpath \
176 bootstrap \ 179 bootstrap \
177 release.nix \ 180 release.nix \
diff --git a/config-daemon.ac b/config-daemon.ac
index 5db08d099dd..0717141198e 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -105,6 +105,8 @@ if test "x$guix_build_daemon" = "xyes"; then
105 [chmod +x nix/scripts/list-runtime-roots]) 105 [chmod +x nix/scripts/list-runtime-roots])
106 AC_CONFIG_FILES([nix/scripts/substitute-binary], 106 AC_CONFIG_FILES([nix/scripts/substitute-binary],
107 [chmod +x nix/scripts/substitute-binary]) 107 [chmod +x nix/scripts/substitute-binary])
108 AC_CONFIG_FILES([nix/scripts/guix-authenticate],
109 [chmod +x nix/scripts/guix-authenticate])
108fi 110fi
109 111
110AM_CONDITIONAL([BUILD_DAEMON], [test "x$guix_build_daemon" = "xyes"]) 112AM_CONDITIONAL([BUILD_DAEMON], [test "x$guix_build_daemon" = "xyes"])
diff --git a/daemon.am b/daemon.am
index 77bfe719870..27c631b2da9 100644
--- a/daemon.am
+++ b/daemon.am
@@ -112,10 +112,10 @@ libstore_a_CPPFLAGS = \
112 -DNIX_DATA_DIR=\"$(datadir)\" \ 112 -DNIX_DATA_DIR=\"$(datadir)\" \
113 -DNIX_STATE_DIR=\"$(localstatedir)/nix\" \ 113 -DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
114 -DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \ 114 -DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
115 -DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \ 115 -DNIX_CONF_DIR=\"$(sysconfdir)/guix\" \
116 -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \ 116 -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
117 -DNIX_BIN_DIR=\"$(bindir)\" \ 117 -DNIX_BIN_DIR=\"$(bindir)\" \
118 -DOPENSSL_PATH="\"openssl\"" 118 -DOPENSSL_PATH="\"guix-authenticate\""
119 119
120libstore_a_CXXFLAGS = \ 120libstore_a_CXXFLAGS = \
121 $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) 121 $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS)
diff --git a/guix/scripts/authenticate.scm b/guix/scripts/authenticate.scm
new file mode 100644
index 00000000000..cbafed79d0f
--- /dev/null
+++ b/guix/scripts/authenticate.scm
@@ -0,0 +1,98 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix scripts authenticate)
20 #:use-module (guix config)
21 #:use-module (guix utils)
22 #:use-module (guix pk-crypto)
23 #:use-module (guix ui)
24 #:use-module (rnrs io ports)
25 #:use-module (ice-9 match)
26 #:export (guix-authenticate))
27
28;;; Commentary:
29;;;
30;;; This program is used internally by the daemon to sign exported archive
31;;; (the 'export-paths' RPC), and to authenticate imported archives (the
32;;; 'import-paths' RPC.)
33;;;
34;;; Code:
35
36(define (read-gcry-sexp file)
37 "Read a gcrypt sexp from FILE and return it."
38 (call-with-input-file file
39 (compose string->gcry-sexp get-string-all)))
40
41(define (read-hash-data file)
42 "Read sha256 hash data from FILE and return it as a gcrypt sexp."
43 (let* ((hex (call-with-input-file file get-string-all))
44 (bv (base16-string->bytevector (string-trim-both hex))))
45 (bytevector->hash-data bv)))
46
47
48;;;
49;;; Entry point with 'openssl'-compatible interface. We support this
50;;; interface because that's what the daemon expects, and we want to leave it
51;;; unmodified currently.
52;;;
53
54(define (guix-authenticate . args)
55 (match args
56 (("rsautl" "-sign" "-inkey" key "-in" hash-file)
57 ;; Sign the hash in HASH-FILE with KEY, and return an sexp that includes
58 ;; both the hash and the actual signature.
59 (let* ((secret-key (read-gcry-sexp key))
60 (data (read-hash-data hash-file)))
61 (format #t
62 "(guix-signature ~a (payload ~a))"
63 (gcry-sexp->string (sign data secret-key))
64 (gcry-sexp->string data))
65 #t))
66 (("rsautl" "-verify" "-inkey" key "-pubin" "-in" signature-file)
67 ;; Read the signature as produced above, check it against KEY, and print
68 ;; the signed data to stdout upon success.
69 (let* ((public-key (read-gcry-sexp key))
70 (sig+data (read-gcry-sexp signature-file))
71 (data (find-sexp-token sig+data 'payload))
72 (signature (find-sexp-token sig+data 'sig-val)))
73 (if (and data signature)
74 (if (verify signature data public-key)
75 (begin
76 (display (bytevector->base16-string
77 (hash-data->bytevector data)))
78 #t) ; success
79 (begin
80 (format (current-error-port)
81 "error: invalid signature: ~a~%"
82 (gcry-sexp->string signature))
83 (exit 1)))
84 (begin
85 (format (current-error-port)
86 "error: corrupt signature data: ~a~%"
87 (gcry-sexp->string sig+data))
88 (exit 1)))))
89 (("--help")
90 (display (_ "Usage: guix authenticate OPTION...
91Sign or verify the signature on the given file. This tool is meant to
92be used internally by 'guix-daemon'.\n")))
93 (("--version")
94 (show-version-and-exit "guix authenticate"))
95 (else
96 (leave (_ "wrong arguments")))))
97
98;;; authenticate.scm ends here
diff --git a/guix/store.scm b/guix/store.scm
index 08b0671b295..4ceca0daa20 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -80,6 +80,8 @@
80 dead-paths 80 dead-paths
81 collect-garbage 81 collect-garbage
82 delete-paths 82 delete-paths
83 import-paths
84 export-paths
83 85
84 current-build-output-port 86 current-build-output-port
85 87
@@ -323,7 +325,30 @@ operate, should the disk become full. Return a server object."
323 ;; The port where build output is sent. 325 ;; The port where build output is sent.
324 (make-parameter (current-error-port))) 326 (make-parameter (current-error-port)))
325 327
326(define (process-stderr server) 328(define* (dump-port in out
329 #:optional len
330 #:key (buffer-size 16384))
331 "Read LEN bytes from IN (or as much as possible if LEN is #f) and write it
332to OUT, using chunks of BUFFER-SIZE bytes."
333 (define buffer
334 (make-bytevector buffer-size))
335
336 (let loop ((total 0)
337 (bytes (get-bytevector-n! in buffer 0
338 (if len
339 (min len buffer-size)
340 buffer-size))))
341 (or (eof-object? bytes)
342 (and len (= total len))
343 (let ((total (+ total bytes)))
344 (put-bytevector out buffer 0 bytes)
345 (loop total
346 (get-bytevector-n! in buffer 0
347 (if len
348 (min (- len total) buffer-size)
349 buffer-size)))))))
350
351(define* (process-stderr server #:optional user-port)
327 "Read standard output and standard error from SERVER, writing it to 352 "Read standard output and standard error from SERVER, writing it to
328CURRENT-BUILD-OUTPUT-PORT. Return #t when SERVER is done sending data, and 353CURRENT-BUILD-OUTPUT-PORT. Return #t when SERVER is done sending data, and
329#f otherwise; in the latter case, the caller should call `process-stderr' 354#f otherwise; in the latter case, the caller should call `process-stderr'
@@ -344,17 +369,30 @@ encoding conversion errors."
344 369
345 (let ((k (read-int p))) 370 (let ((k (read-int p)))
346 (cond ((= k %stderr-write) 371 (cond ((= k %stderr-write)
347 (read-latin1-string p) 372 ;; Write a byte stream to USER-PORT.
373 (let* ((len (read-int p))
374 (m (modulo len 8)))
375 (dump-port p user-port len)
376 (unless (zero? m)
377 ;; Consume padding, as for strings.
378 (get-bytevector-n p (- 8 m))))
348 #f) 379 #f)
349 ((= k %stderr-read) 380 ((= k %stderr-read)
350 (let ((len (read-int p))) 381 ;; Read a byte stream from USER-PORT.
351 (read-latin1-string p) ; FIXME: what to do? 382 (let* ((max-len (read-int p))
383 (data (get-bytevector-n user-port max-len))
384 (len (bytevector-length data)))
385 (write-int len p)
386 (put-bytevector p data)
387 (write-padding len p)
352 #f)) 388 #f))
353 ((= k %stderr-next) 389 ((= k %stderr-next)
390 ;; Log a string.
354 (let ((s (read-latin1-string p))) 391 (let ((s (read-latin1-string p)))
355 (display s (current-build-output-port)) 392 (display s (current-build-output-port))
356 #f)) 393 #f))
357 ((= k %stderr-error) 394 ((= k %stderr-error)
395 ;; Report an error.
358 (let ((error (read-latin1-string p)) 396 (let ((error (read-latin1-string p))
359 ;; Currently the daemon fails to send a status code for early 397 ;; Currently the daemon fails to send a status code for early
360 ;; errors like DB schema version mismatches, so check for EOF. 398 ;; errors like DB schema version mismatches, so check for EOF.
@@ -624,6 +662,39 @@ MIN-FREED bytes have been collected. Return the paths that were
624collected, and the number of bytes freed." 662collected, and the number of bytes freed."
625 (run-gc server (gc-action delete-specific) paths min-freed)) 663 (run-gc server (gc-action delete-specific) paths min-freed))
626 664
665(define (import-paths server port)
666 "Import the set of store paths read from PORT into SERVER's store. An error
667is raised if the set of paths read from PORT is not signed (as per
668'export-path #:sign? #t'.) Return the list of store paths imported."
669 (let ((s (nix-server-socket server)))
670 (write-int (operation-id import-paths) s)
671 (let loop ((done? (process-stderr server port)))
672 (or done? (loop (process-stderr server port))))
673 (read-store-path-list s)))
674
675(define* (export-path server path port #:key (sign? #t))
676 "Export PATH to PORT. When SIGN? is true, sign it."
677 (let ((s (nix-server-socket server)))
678 (write-int (operation-id export-path) s)
679 (write-store-path path s)
680 (write-arg boolean sign? s)
681 (let loop ((done? (process-stderr server port)))
682 (or done? (loop (process-stderr server port))))
683 (= 1 (read-int s))))
684
685(define* (export-paths server paths port #:key (sign? #t))
686 "Export the store paths listed in PATHS to PORT, signing them if SIGN?
687is true."
688 (let ((s (nix-server-socket server)))
689 (let loop ((paths paths))
690 (match paths
691 (()
692 (write-int 0 port))
693 ((head tail ...)
694 (write-int 1 port)
695 (and (export-path server head port #:sign? sign?)
696 (loop tail)))))))
697
627 698
628;;; 699;;;
629;;; Store paths. 700;;; Store paths.
diff --git a/nix/nix-daemon/guix-daemon.cc b/nix/nix-daemon/guix-daemon.cc
index 484a3909368..cf87e393545 100644
--- a/nix/nix-daemon/guix-daemon.cc
+++ b/nix/nix-daemon/guix-daemon.cc
@@ -216,6 +216,12 @@ main (int argc, char *argv[])
216 { 216 {
217 settings.processEnvironment (); 217 settings.processEnvironment ();
218 218
219 /* Hackily help 'local-store.cc' find our 'guix-authenticate' program, which
220 is known as 'OPENSSL_PATH' here. */
221 std::string search_path (getenv ("PATH"));
222 search_path = settings.nixLibexecDir + ":" + search_path;
223 setenv ("PATH", search_path.c_str (), 1);
224
219 /* Use our substituter by default. */ 225 /* Use our substituter by default. */
220 settings.substituters.clear (); 226 settings.substituters.clear ();
221 settings.useSubstitutes = true; 227 settings.useSubstitutes = true;
diff --git a/nix/scripts/guix-authenticate.in b/nix/scripts/guix-authenticate.in
new file mode 100644
index 00000000000..5ce57915f02
--- /dev/null
+++ b/nix/scripts/guix-authenticate.in
@@ -0,0 +1,11 @@
1#!@SHELL@
2# A shorthand for "guix authenticate", for use by the daemon.
3
4if test "x$GUIX_UNINSTALLED" = "x"
5then
6 prefix="@prefix@"
7 exec_prefix="@exec_prefix@"
8 exec "@bindir@/guix" authenticate "$@"
9else
10 exec guix authenticate "$@"
11fi
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0e30bb08804..beefdc901b8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ guix/scripts/gc.scm
11guix/scripts/hash.scm 11guix/scripts/hash.scm
12guix/scripts/pull.scm 12guix/scripts/pull.scm
13guix/scripts/substitute-binary.scm 13guix/scripts/substitute-binary.scm
14guix/scripts/authenticate.scm
14guix/gnu-maintenance.scm 15guix/gnu-maintenance.scm
15guix/ui.scm 16guix/ui.scm
16guix/http-client.scm 17guix/http-client.scm
diff --git a/test-env.in b/test-env.in
index 9224a805377..df73ecdc7a2 100644
--- a/test-env.in
+++ b/test-env.in
@@ -40,6 +40,22 @@ then
40 # Currently, in Nix builds, we're at ~106 chars... 40 # Currently, in Nix builds, we're at ~106 chars...
41 NIX_STATE_DIR="@GUIX_TEST_ROOT@/var/$$" 41 NIX_STATE_DIR="@GUIX_TEST_ROOT@/var/$$"
42 42
43 # The configuration directory, for import/export signing keys.
44 NIX_CONF_DIR="@GUIX_TEST_ROOT@/etc"
45 if [ ! -d "$NIX_CONF_DIR" ]
46 then
47 # Copy the keys so that the secret key has the right permissions (the
48 # daemon errors out when this is not the case.)
49 mkdir -p "$NIX_CONF_DIR"
50 cp "@abs_top_srcdir@/tests/signing-key.sec" \
51 "@abs_top_srcdir@/tests/signing-key.pub" \
52 "$NIX_CONF_DIR"
53 chmod 400 "$NIX_CONF_DIR/signing-key.sec"
54 fi
55
56 # For 'guix-authenticate'.
57 NIX_LIBEXEC_DIR="@abs_top_builddir@/nix/scripts"
58
43 # A place to store data of the substituter. 59 # A place to store data of the substituter.
44 GUIX_BINARY_SUBSTITUTE_URL="file://$NIX_STATE_DIR/substituter-data" 60 GUIX_BINARY_SUBSTITUTE_URL="file://$NIX_STATE_DIR/substituter-data"
45 rm -rf "$NIX_STATE_DIR/substituter-data" 61 rm -rf "$NIX_STATE_DIR/substituter-data"
@@ -51,7 +67,7 @@ then
51 export NIX_IGNORE_SYMLINK_STORE NIX_STORE_DIR \ 67 export NIX_IGNORE_SYMLINK_STORE NIX_STORE_DIR \
52 NIX_LOCALSTATE_DIR NIX_LOG_DIR NIX_STATE_DIR NIX_DB_DIR \ 68 NIX_LOCALSTATE_DIR NIX_LOG_DIR NIX_STATE_DIR NIX_DB_DIR \
53 NIX_ROOT_FINDER NIX_SETUID_HELPER GUIX_BINARY_SUBSTITUTE_URL \ 69 NIX_ROOT_FINDER NIX_SETUID_HELPER GUIX_BINARY_SUBSTITUTE_URL \
54 XDG_CACHE_HOME 70 NIX_CONF_DIR NIX_LIBEXEC_DIR XDG_CACHE_HOME
55 71
56 # Do that because store.scm calls `canonicalize-path' on it. 72 # Do that because store.scm calls `canonicalize-path' on it.
57 mkdir -p "$NIX_STORE_DIR" 73 mkdir -p "$NIX_STORE_DIR"
diff --git a/tests/signing-key.pub b/tests/signing-key.pub
new file mode 100644
index 00000000000..092424a15d2
--- /dev/null
+++ b/tests/signing-key.pub
@@ -0,0 +1,4 @@
1(public-key
2 (rsa
3 (n #00C1F764069F54FFE93A126B02328903E984E4AE3AF6DF402B5B6B3907911B88C385F1BA76A002EC9DEA109A5228EF0E62EE31A06D1A5861CAB474F6C857AC66EB65A1905F25BBA1869579E73A3B7FED13AF5A1667326F88CDFC2FF24B03C14FD1384AA7E73CA89572880B606E3A974E15347963FC7B6378574936A47580DBCB45#)
4 (e #010001#)))
diff --git a/tests/signing-key.sec b/tests/signing-key.sec
new file mode 100644
index 00000000000..558e1891027
--- /dev/null
+++ b/tests/signing-key.sec
@@ -0,0 +1,8 @@
1(private-key
2 (rsa
3 (n #00C1F764069F54FFE93A126B02328903E984E4AE3AF6DF402B5B6B3907911B88C385F1BA76A002EC9DEA109A5228EF0E62EE31A06D1A5861CAB474F6C857AC66EB65A1905F25BBA1869579E73A3B7FED13AF5A1667326F88CDFC2FF24B03C14FD1384AA7E73CA89572880B606E3A974E15347963FC7B6378574936A47580DBCB45#)
4 (e #010001#)
5 (d #58CAD84653D0046A8EC3F9AA82D9C829B145422109FC3F12DA01A694B92FA296E70D366FB166454D30E632CEE3A033B4C41781BA10325F69FCDC0250CA19C8EEB352FA085992494098DB133E682ED38A931701F0DED1A1E508F4341A4FB446A04F019427C7CB3C44F251EEA9D386100DA80F125E0FD5CE1B0DFEC6D21516EACD#)
6 (p #00D47F185147EC39393CCDA4E7323FFC20FC8B8073E2A54DD63BA392A66975E4204CA48572496A9DFD7522436B852C07472A5AB25B7706F7C14E6F33FBC420FF3B#)
7 (q #00E9AD22F158060BC9AE3601DA623AFC60FFF3058795802CA92371C00097335CF9A23D7782DE353C9DBA93D7BB99E6A24A411107605E722481C5C191F80D7EB77F#)
8 (u #59B45B95AE01A7A7370FAFDB08FE73A4793CE37F228961B09B1B1E7DDAD9F8D3E28F5C5E8B4B067E6B8E0BBF3F690B42991A79E46108DDCDA2514323A66964DE#)))
diff --git a/tests/store.scm b/tests/store.scm
index 281b923c283..6834ebc5e97 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -28,10 +28,12 @@
28 #:use-module (gnu packages) 28 #:use-module (gnu packages)
29 #:use-module (gnu packages bootstrap) 29 #:use-module (gnu packages bootstrap)
30 #:use-module (ice-9 match) 30 #:use-module (ice-9 match)
31 #:use-module (rnrs bytevectors)
31 #:use-module (rnrs io ports) 32 #:use-module (rnrs io ports)
32 #:use-module (web uri) 33 #:use-module (web uri)
33 #:use-module (srfi srfi-1) 34 #:use-module (srfi srfi-1)
34 #:use-module (srfi srfi-11) 35 #:use-module (srfi srfi-11)
36 #:use-module (srfi srfi-26)
35 #:use-module (srfi srfi-34) 37 #:use-module (srfi srfi-34)
36 #:use-module (srfi srfi-64)) 38 #:use-module (srfi srfi-64))
37 39
@@ -344,6 +346,49 @@ Deriver: ~a~%"
344 (build-derivations s (list d)) 346 (build-derivations s (list d))
345 #f)))) 347 #f))))
346 348
349(test-assert "export/import several paths"
350 (let* ((texts (unfold (cut >= <> 10)
351 (lambda _ (random-text))
352 1+
353 0))
354 (files (map (cut add-text-to-store %store "text" <>) texts))
355 (dump (call-with-bytevector-output-port
356 (cut export-paths %store files <>))))
357 (delete-paths %store files)
358 (and (every (negate file-exists?) files)
359 (let* ((source (open-bytevector-input-port dump))
360 (imported (import-paths %store source)))
361 (and (equal? imported files)
362 (every file-exists? files)
363 (equal? texts
364 (map (lambda (file)
365 (call-with-input-file file
366 get-string-all))
367 files)))))))
368
369(test-assert "import corrupt path"
370 (let* ((text (random-text))
371 (file (add-text-to-store %store "text" text))
372 (dump (call-with-bytevector-output-port
373 (cut export-paths %store (list file) <>))))
374 (delete-paths %store (list file))
375
376 ;; Flip a bit in the middle of the stream.
377 (let* ((index (quotient (bytevector-length dump) 3))
378 (byte (bytevector-u8-ref dump index)))
379 (bytevector-u8-set! dump index (logxor #xff byte)))
380
381 (and (not (file-exists? file))
382 (guard (c ((nix-protocol-error? c)
383 (pk 'c c)
384 (and (not (zero? (nix-protocol-error-status c)))
385 (string-contains (nix-protocol-error-message c)
386 "corrupt"))))
387 (let* ((source (open-bytevector-input-port dump))
388 (imported (import-paths %store source)))
389 (pk 'corrupt-imported imported)
390 #f)))))
391
347(test-end "store") 392(test-end "store")
348 393
349 394