daemon: Add `--no-substitutes'.
[jackhill/guix/guix.git] / guix / store.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
34fcbe3a 2;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
77d3cf08 3;;;
233e7676 4;;; This file is part of GNU Guix.
77d3cf08 5;;;
233e7676 6;;; GNU Guix is free software; you can redistribute it and/or modify it
77d3cf08
LC
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;;;
233e7676 11;;; GNU Guix is distributed in the hope that it will be useful, but
77d3cf08
LC
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
233e7676 17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
77d3cf08
LC
18
19(define-module (guix store)
0f41c26f 20 #:use-module (guix nar)
82058eff 21 #:use-module (guix utils)
d8eea3d2 22 #:use-module (guix config)
0f41c26f 23 #:use-module (guix serialization)
77d3cf08
LC
24 #:use-module (rnrs bytevectors)
25 #:use-module (rnrs io ports)
26 #:use-module (srfi srfi-1)
27 #:use-module (srfi srfi-9)
28 #:use-module (srfi srfi-26)
e87088c9
LC
29 #:use-module (srfi srfi-34)
30 #:use-module (srfi srfi-35)
26bbbb95 31 #:use-module (srfi srfi-39)
77d3cf08 32 #:use-module (ice-9 match)
e3d74106 33 #:use-module (ice-9 regex)
9fd72fb1
LC
34 #:export (%daemon-socket-file
35
36 nix-server?
77d3cf08
LC
37 nix-server-major-version
38 nix-server-minor-version
39 nix-server-socket
40
e87088c9 41 &nix-error nix-error?
ef86c39f
LC
42 &nix-connection-error nix-connection-error?
43 nix-connection-error-file
44 nix-connection-error-code
e87088c9
LC
45 &nix-protocol-error nix-protocol-error?
46 nix-protocol-error-message
47 nix-protocol-error-status
48
26bbbb95
LC
49 hash-algo
50
77d3cf08 51 open-connection
3abaf0c4 52 close-connection
77d3cf08 53 set-build-options
31ef99a8 54 valid-path?
82058eff 55 query-path-hash
77d3cf08
LC
56 add-text-to-store
57 add-to-store
26bbbb95 58 build-derivations
3259877d 59 add-temp-root
34811f02 60 add-indirect-root
26bbbb95 61
0f3d2504
LC
62 substitutable?
63 substitutable-path
64 substitutable-deriver
65 substitutable-references
66 substitutable-download-size
67 substitutable-nar-size
68 has-substitutes?
69 substitutable-paths
70 substitutable-path-info
71
fae31edc
LC
72 references
73 referrers
74 valid-derivers
75 query-derivation-outputs
3259877d
LC
76 live-paths
77 dead-paths
78 collect-garbage
79 delete-paths
80
dcee50c1
LC
81 current-build-output-port
82
26bbbb95
LC
83 %store-prefix
84 store-path?
e3d74106 85 derivation-path?
2c6ab6cc
LC
86 store-path-package-name
87 store-path-hash-part))
77d3cf08 88
63193ebf 89(define %protocol-version #x10c)
77d3cf08
LC
90
91(define %worker-magic-1 #x6e697863)
92(define %worker-magic-2 #x6478696f)
93
94(define (protocol-major magic)
95 (logand magic #xff00))
96(define (protocol-minor magic)
97 (logand magic #x00ff))
98
99(define-syntax define-enumerate-type
100 (syntax-rules ()
101 ((_ name->int (name id) ...)
102 (define-syntax name->int
103 (syntax-rules (name ...)
104 ((_ name) id) ...)))))
105
106(define-enumerate-type operation-id
107 ;; operation numbers from worker-protocol.hh
108 (quit 0)
109 (valid-path? 1)
110 (has-substitutes? 3)
111 (query-path-hash 4)
112 (query-references 5)
113 (query-referrers 6)
114 (add-to-store 7)
115 (add-text-to-store 8)
116 (build-derivations 9)
117 (ensure-path 10)
118 (add-temp-root 11)
119 (add-indirect-root 12)
120 (sync-with-gc 13)
121 (find-roots 14)
122 (export-path 16)
123 (query-deriver 18)
124 (set-options 19)
125 (collect-garbage 20)
63193ebf 126 ;;(query-substitutable-path-info 21) ; obsolete as of #x10c
77d3cf08 127 (query-derivation-outputs 22)
63193ebf 128 (query-all-valid-paths 23)
77d3cf08
LC
129 (query-failed-paths 24)
130 (clear-failed-paths 25)
131 (query-path-info 26)
132 (import-paths 27)
63193ebf
LC
133 (query-derivation-output-names 28)
134 (query-path-from-hash-part 29)
135 (query-substitutable-path-infos 30)
136 (query-valid-paths 31)
fae31edc
LC
137 (query-substitutable-paths 32)
138 (query-valid-derivers 33))
77d3cf08
LC
139
140(define-enumerate-type hash-algo
141 ;; hash.hh
142 (md5 1)
143 (sha1 2)
144 (sha256 3))
145
3259877d
LC
146(define-enumerate-type gc-action
147 ;; store-api.hh
148 (return-live 0)
149 (return-dead 1)
150 (delete-dead 2)
151 (delete-specific 3))
152
77d3cf08 153(define %default-socket-path
d8eea3d2
LC
154 (string-append (or (getenv "NIX_STATE_DIR") %state-directory)
155 "/daemon-socket/socket"))
77d3cf08 156
9fd72fb1
LC
157(define %daemon-socket-file
158 ;; File name of the socket the daemon listens too.
159 (make-parameter (or (getenv "GUIX_DAEMON_SOCKET")
160 %default-socket-path)))
161
162
77d3cf08 163\f
0f3d2504
LC
164;; Information about a substitutable store path.
165(define-record-type <substitutable>
166 (substitutable path deriver refs dl-size nar-size)
167 substitutable?
168 (path substitutable-path)
169 (deriver substitutable-deriver)
170 (refs substitutable-references)
171 (dl-size substitutable-download-size)
172 (nar-size substitutable-nar-size))
173
174(define (read-substitutable-path-list p)
175 (let loop ((len (read-int p))
176 (result '()))
177 (if (zero? len)
178 (reverse result)
179 (let ((path (read-store-path p))
180 (deriver (read-store-path p))
181 (refs (read-store-path-list p))
182 (dl-size (read-long-long p))
183 (nar-size (read-long-long p)))
184 (loop (- len 1)
185 (cons (substitutable path deriver refs dl-size nar-size)
186 result))))))
187
77d3cf08 188(define-syntax write-arg
3259877d
LC
189 (syntax-rules (integer boolean file string string-list
190 store-path store-path-list base16)
77d3cf08
LC
191 ((_ integer arg p)
192 (write-int arg p))
193 ((_ boolean arg p)
194 (write-int (if arg 1 0) p))
195 ((_ file arg p)
196 (write-file arg p))
197 ((_ string arg p)
198 (write-string arg p))
199 ((_ string-list arg p)
82058eff 200 (write-string-list arg p))
3259877d
LC
201 ((_ store-path arg p)
202 (write-store-path arg p))
203 ((_ store-path-list arg p)
204 (write-store-path-list arg p))
82058eff
LC
205 ((_ base16 arg p)
206 (write-string (bytevector->base16-string arg) p))))
77d3cf08
LC
207
208(define-syntax read-arg
0f3d2504
LC
209 (syntax-rules (integer boolean string store-path store-path-list
210 substitutable-path-list base16)
77d3cf08
LC
211 ((_ integer p)
212 (read-int p))
213 ((_ boolean p)
214 (not (zero? (read-int p))))
215 ((_ string p)
216 (read-string p))
217 ((_ store-path p)
82058eff 218 (read-store-path p))
3259877d
LC
219 ((_ store-path-list p)
220 (read-store-path-list p))
0f3d2504
LC
221 ((_ substitutable-path-list p)
222 (read-substitutable-path-list p))
223 ((_ base16 p)
82058eff 224 (base16-string->bytevector (read-string p)))))
77d3cf08
LC
225
226\f
227;; remote-store.cc
228
229(define-record-type <nix-server>
2c3f47ee 230 (%make-nix-server socket major minor
bdcf35a6 231 ats-cache atts-cache)
77d3cf08
LC
232 nix-server?
233 (socket nix-server-socket)
234 (major nix-server-major-version)
2c3f47ee
LC
235 (minor nix-server-minor-version)
236
237 ;; Caches. We keep them per-connection, because store paths build
238 ;; during the session are temporary GC roots kept for the duration of
239 ;; the session.
bdcf35a6
LC
240 (ats-cache nix-server-add-to-store-cache)
241 (atts-cache nix-server-add-text-to-store-cache))
77d3cf08 242
e87088c9
LC
243(define-condition-type &nix-error &error
244 nix-error?)
245
ef86c39f
LC
246(define-condition-type &nix-connection-error &nix-error
247 nix-connection-error?
248 (file nix-connection-error-file)
249 (errno nix-connection-error-code))
250
e87088c9
LC
251(define-condition-type &nix-protocol-error &nix-error
252 nix-protocol-error?
253 (message nix-protocol-error-message)
254 (status nix-protocol-error-status))
255
9fd72fb1 256(define* (open-connection #:optional (file (%daemon-socket-file))
e36a7172 257 #:key (reserve-space? #t))
e531ac2a
LC
258 "Connect to the daemon over the Unix-domain socket at FILE. When
259RESERVE-SPACE? is true, instruct it to reserve a little bit of extra
260space on the file system so that the garbage collector can still
261operate, should the disk become full. Return a server object."
77d3cf08
LC
262 (let ((s (with-fluids ((%default-port-encoding #f))
263 ;; This trick allows use of the `scm_c_read' optimization.
264 (socket PF_UNIX SOCK_STREAM 0)))
265 (a (make-socket-address PF_UNIX file)))
df1fab58
LC
266
267 ;; Enlarge the receive buffer.
268 (setsockopt s SOL_SOCKET SO_RCVBUF (* 12 1024))
269
ef86c39f
LC
270 (catch 'system-error
271 (cut connect s a)
272 (lambda args
273 ;; Translate the error to something user-friendly.
274 (let ((errno (system-error-errno args)))
275 (raise (condition (&nix-connection-error
276 (file file)
277 (errno errno)))))))
278
77d3cf08
LC
279 (write-int %worker-magic-1 s)
280 (let ((r (read-int s)))
281 (and (eqv? r %worker-magic-2)
282 (let ((v (read-int s)))
283 (and (eqv? (protocol-major %protocol-version)
284 (protocol-major v))
285 (begin
286 (write-int %protocol-version s)
e36a7172
LC
287 (if (>= (protocol-minor v) 11)
288 (write-int (if reserve-space? 1 0) s))
77d3cf08
LC
289 (let ((s (%make-nix-server s
290 (protocol-major v)
2c3f47ee 291 (protocol-minor v)
fce2394e
LC
292 (make-hash-table 100)
293 (make-hash-table 100))))
34fcbe3a
LC
294 (let loop ((done? (process-stderr s)))
295 (or done? (process-stderr s)))
77d3cf08
LC
296 s))))))))
297
3abaf0c4
LC
298(define (close-connection server)
299 "Close the connection to SERVER."
300 (close (nix-server-socket server)))
301
dcee50c1
LC
302(define current-build-output-port
303 ;; The port where build output is sent.
304 (make-parameter (current-error-port)))
305
77d3cf08 306(define (process-stderr server)
dcee50c1
LC
307 "Read standard output and standard error from SERVER, writing it to
308CURRENT-BUILD-OUTPUT-PORT. Return #t when SERVER is done sending data, and
309#f otherwise; in the latter case, the caller should call `process-stderr'
bbdb3ffa
LC
310again until #t is returned or an error is raised.
311
312Since the build process's output cannot be assumed to be UTF-8, we
313conservatively consider it to be Latin-1, thereby avoiding possible
314encoding conversion errors."
77d3cf08
LC
315 (define p
316 (nix-server-socket server))
317
318 ;; magic cookies from worker-protocol.hh
319 (define %stderr-next #x6f6c6d67)
320 (define %stderr-read #x64617461) ; data needed from source
321 (define %stderr-write #x64617416) ; data for sink
322 (define %stderr-last #x616c7473)
323 (define %stderr-error #x63787470)
324
325 (let ((k (read-int p)))
326 (cond ((= k %stderr-write)
bbdb3ffa 327 (read-latin1-string p)
dcee50c1 328 #f)
77d3cf08
LC
329 ((= k %stderr-read)
330 (let ((len (read-int p)))
bbdb3ffa 331 (read-latin1-string p) ; FIXME: what to do?
dcee50c1 332 #f))
77d3cf08 333 ((= k %stderr-next)
bbdb3ffa 334 (let ((s (read-latin1-string p)))
dcee50c1
LC
335 (display s (current-build-output-port))
336 #f))
77d3cf08 337 ((= k %stderr-error)
bbdb3ffa 338 (let ((error (read-latin1-string p))
77d3cf08
LC
339 (status (if (>= (nix-server-minor-version server) 8)
340 (read-int p)
341 1)))
e87088c9
LC
342 (raise (condition (&nix-protocol-error
343 (message error)
344 (status status))))))
77d3cf08 345 ((= k %stderr-last)
dcee50c1 346 ;; The daemon is done (see `stopWork' in `nix-worker.cc'.)
77d3cf08
LC
347 #t)
348 (else
e87088c9
LC
349 (raise (condition (&nix-protocol-error
350 (message "invalid error code")
351 (status k))))))))
77d3cf08
LC
352
353(define* (set-build-options server
354 #:key keep-failed? keep-going? try-fallback?
355 (verbosity 0)
356 (max-build-jobs (current-processor-count))
357 (max-silent-time 3600)
358 (use-build-hook? #t)
359 (build-verbosity 0)
360 (log-type 0)
e036c31b
LC
361 (print-build-trace #t)
362 (build-cores 1)
63193ebf
LC
363 (use-substitutes? #t)
364 (binary-caches '())) ; client "untrusted" cache URLs
77d3cf08
LC
365 ;; Must be called after `open-connection'.
366
367 (define socket
368 (nix-server-socket server))
369
370 (let-syntax ((send (syntax-rules ()
e036c31b
LC
371 ((_ (type option) ...)
372 (begin
373 (write-arg type option socket)
374 ...)))))
375 (write-int (operation-id set-options) socket)
376 (send (boolean keep-failed?) (boolean keep-going?)
377 (boolean try-fallback?) (integer verbosity)
378 (integer max-build-jobs) (integer max-silent-time))
77d3cf08 379 (if (>= (nix-server-minor-version server) 2)
e036c31b 380 (send (boolean use-build-hook?)))
77d3cf08 381 (if (>= (nix-server-minor-version server) 4)
e036c31b
LC
382 (send (integer build-verbosity) (integer log-type)
383 (boolean print-build-trace)))
384 (if (>= (nix-server-minor-version server) 6)
385 (send (integer build-cores)))
386 (if (>= (nix-server-minor-version server) 10)
387 (send (boolean use-substitutes?)))
63193ebf
LC
388 (if (>= (nix-server-minor-version server) 12)
389 (send (string-list (fold-right (lambda (pair result)
390 (match pair
391 ((h . t)
392 (cons* h t result))))
393 '()
394 binary-caches))))
dcee50c1
LC
395 (let loop ((done? (process-stderr server)))
396 (or done? (process-stderr server)))))
77d3cf08 397
fd060fd3 398(define-syntax operation
77d3cf08 399 (syntax-rules ()
fd060fd3 400 "Define a client-side RPC stub for the given operation."
3259877d 401 ((_ (name (type arg) ...) docstring return ...)
fd060fd3 402 (lambda (server arg ...)
77d3cf08
LC
403 docstring
404 (let ((s (nix-server-socket server)))
405 (write-int (operation-id name) s)
406 (write-arg type arg s)
407 ...
dcee50c1
LC
408 ;; Loop until the server is done sending error output.
409 (let loop ((done? (process-stderr server)))
410 (or done? (loop (process-stderr server))))
3259877d 411 (values (read-arg return s) ...))))))
77d3cf08 412
fd060fd3
LC
413(define-syntax-rule (define-operation (name args ...)
414 docstring return ...)
415 (define name
416 (operation (name args ...) docstring return ...)))
417
31ef99a8
LC
418(define-operation (valid-path? (string path))
419 "Return #t when PATH is a valid store path."
420 boolean)
421
63193ebf 422(define-operation (query-path-hash (store-path path))
82058eff
LC
423 "Return the SHA256 hash of PATH as a bytevector."
424 base16)
425
fd060fd3
LC
426(define add-text-to-store
427 ;; A memoizing version of `add-to-store', to avoid repeated RPCs with
428 ;; the very same arguments during a given session.
429 (let ((add-text-to-store
430 (operation (add-text-to-store (string name) (string text)
431 (string-list references))
432 #f
433 store-path)))
bdcf35a6
LC
434 (lambda (server name text references)
435 "Add TEXT under file NAME in the store, and return its store path.
436REFERENCES is the list of store paths referred to by the resulting store
437path."
fce2394e 438 (let ((args `(,text ,name ,references))
bdcf35a6
LC
439 (cache (nix-server-add-text-to-store-cache server)))
440 (or (hash-ref cache args)
fd060fd3 441 (let ((path (add-text-to-store server name text references)))
bdcf35a6
LC
442 (hash-set! cache args path)
443 path))))))
444
fd060fd3 445(define add-to-store
a7b6ffee
LC
446 ;; A memoizing version of `add-to-store'. This is important because
447 ;; `add-to-store' leads to huge data transfers to the server, and
448 ;; because it's often called many times with the very same argument.
fd060fd3
LC
449 (let ((add-to-store (operation (add-to-store (string basename)
450 (boolean fixed?) ; obsolete, must be #t
451 (boolean recursive?)
452 (string hash-algo)
453 (file file-name))
454 #f
455 store-path)))
a9ebd9ef
LC
456 (lambda (server basename recursive? hash-algo file-name)
457 "Add the contents of FILE-NAME under BASENAME to the store. When
458RECURSIVE? is true and FILE-NAME designates a directory, the contents of
459FILE-NAME are added recursively; if FILE-NAME designates a flat file and
460RECURSIVE? is true, its contents are added, and its permission bits are
461kept. HASH-ALGO must be a string such as \"sha256\"."
2c3f47ee 462 (let* ((st (stat file-name #f))
fce2394e 463 (args `(,st ,basename ,recursive? ,hash-algo))
2c3f47ee 464 (cache (nix-server-add-to-store-cache server)))
a7b6ffee 465 (or (and st (hash-ref cache args))
a9ebd9ef 466 (let ((path (add-to-store server basename #t recursive?
a7b6ffee
LC
467 hash-algo file-name)))
468 (hash-set! cache args path)
469 path))))))
470
77d3cf08 471(define-operation (build-derivations (string-list derivations))
dcee50c1
LC
472 "Build DERIVATIONS, and return when the worker is done building them.
473Return #t on success."
77d3cf08 474 boolean)
26bbbb95 475
d3648e01
LC
476(define-operation (add-temp-root (store-path path))
477 "Make PATH a temporary root for the duration of the current session.
478Return #t."
479 boolean)
480
34811f02
LC
481(define-operation (add-indirect-root (string file-name))
482 "Make FILE-NAME an indirect root for the garbage collector; FILE-NAME
483can be anywhere on the file system, but it must be an absolute file
484name--it is the caller's responsibility to ensure that it is an absolute
485file name. Return #t on success."
486 boolean)
487
fae31edc
LC
488(define references
489 (operation (query-references (store-path path))
490 "Return the list of references of PATH."
491 store-path-list))
492
493(define referrers
494 (operation (query-referrers (store-path path))
495 "Return the list of path that refer to PATH."
496 store-path-list))
497
498(define valid-derivers
499 (operation (query-valid-derivers (store-path path))
500 "Return the list of valid \"derivers\" of PATH---i.e., all the
501.drv present in the store that have PATH among their outputs."
502 store-path-list))
503
504(define query-derivation-outputs ; avoid name clash with `derivation-outputs'
505 (operation (query-derivation-outputs (store-path path))
506 "Return the list of outputs of PATH, a .drv file."
507 store-path-list))
508
0f3d2504
LC
509(define-operation (has-substitutes? (store-path path))
510 "Return #t if binary substitutes are available for PATH, and #f otherwise."
511 boolean)
512
513(define substitutable-paths
514 (operation (query-substitutable-paths (store-path-list paths))
515 "Return the subset of PATHS that is substitutable."
516 store-path-list))
517
518(define substitutable-path-info
f65cf81a 519 (operation (query-substitutable-path-infos (store-path-list paths))
0f3d2504
LC
520 "Return information about the subset of PATHS that is
521substitutable. For each substitutable path, a `substitutable?' object is
522returned."
523 substitutable-path-list))
524
3259877d
LC
525(define (run-gc server action to-delete min-freed)
526 "Perform the garbage-collector operation ACTION, one of the
527`gc-action' values. When ACTION is `delete-specific', the TO-DELETE is
528the list of store paths to delete. IGNORE-LIVENESS? should always be
529#f. MIN-FREED is the minimum amount of disk space to be freed, in
530bytes, before the GC can stop. Return the list of store paths delete,
531and the number of bytes freed."
532 (let ((s (nix-server-socket server)))
533 (write-int (operation-id collect-garbage) s)
534 (write-int action s)
535 (write-store-path-list to-delete s)
536 (write-arg boolean #f s) ; ignore-liveness?
537 (write-long-long min-freed s)
538 (write-int 0 s) ; obsolete
539 (when (>= (nix-server-minor-version server) 5)
540 ;; Obsolete `use-atime' and `max-atime' parameters.
541 (write-int 0 s)
542 (write-int 0 s))
543
544 ;; Loop until the server is done sending error output.
545 (let loop ((done? (process-stderr server)))
546 (or done? (loop (process-stderr server))))
547
548 (let ((paths (read-store-path-list s))
549 (freed (read-long-long s))
550 (obsolete (read-long-long s)))
551 (values paths freed))))
552
553(define-syntax-rule (%long-long-max)
554 ;; Maximum unsigned 64-bit integer.
555 (- (expt 2 64) 1))
556
557(define (live-paths server)
558 "Return the list of live store paths---i.e., store paths still
559referenced, and thus not subject to being garbage-collected."
560 (run-gc server (gc-action return-live) '() (%long-long-max)))
561
562(define (dead-paths server)
563 "Return the list of dead store paths---i.e., store paths no longer
564referenced, and thus subject to being garbage-collected."
565 (run-gc server (gc-action return-dead) '() (%long-long-max)))
566
567(define* (collect-garbage server #:optional (min-freed (%long-long-max)))
568 "Collect garbage from the store at SERVER. If MIN-FREED is non-zero,
569then collect at least MIN-FREED bytes. Return the paths that were
570collected, and the number of bytes freed."
571 (run-gc server (gc-action delete-dead) '() min-freed))
572
573(define* (delete-paths server paths #:optional (min-freed (%long-long-max)))
574 "Delete PATHS from the store at SERVER, if they are no longer
575referenced. If MIN-FREED is non-zero, then stop after at least
576MIN-FREED bytes have been collected. Return the paths that were
577collected, and the number of bytes freed."
578 (run-gc server (gc-action delete-specific) paths min-freed))
579
26bbbb95
LC
580\f
581;;;
582;;; Store paths.
583;;;
584
585(define %store-prefix
586 ;; Absolute path to the Nix store.
cd3ded43 587 (make-parameter (or (and=> (getenv "NIX_STORE_DIR") canonicalize-path)
d8eea3d2 588 %store-directory)))
26bbbb95 589
f39bd08a
LC
590(define (store-path? path)
591 "Return #t if PATH is a store path."
592 ;; This is a lightweight check, compared to using a regexp, but this has to
593 ;; be fast as it's called often in `derivation', for instance.
594 ;; `isStorePath' in Nix does something similar.
595 (string-prefix? (%store-prefix) path))
26bbbb95
LC
596
597(define (derivation-path? path)
598 "Return #t if PATH is a derivation path."
599 (and (store-path? path) (string-suffix? ".drv" path)))
e3d74106
LC
600
601(define (store-path-package-name path)
602 "Return the package name part of PATH, a file name in the store."
603 (define store-path-rx
604 (make-regexp (string-append "^.*" (regexp-quote (%store-prefix))
605 "/[^-]+-(.+)$")))
606
607 (and=> (regexp-exec store-path-rx path)
608 (cut match:substring <> 1)))
2c6ab6cc
LC
609
610(define (store-path-hash-part path)
611 "Return the hash part of PATH as a base32 string, or #f if PATH is not a
612syntactically valid store path."
613 (let ((path-rx (make-regexp
614 (string-append"^" (regexp-quote (%store-prefix))
615 "/([0-9a-df-np-sv-z]{32})-[^/]+$"))))
616 (and=> (regexp-exec path-rx path)
617 (cut match:substring <> 1))))