diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2017-05-18 11:35:45 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2017-05-20 10:17:14 +0200 |
| commit | 36c99429a3638305c16f1e6f5e087daa174d249c (patch) | |
| tree | d136f763a920b276ea60500e677005bc5e72f669 | |
| parent | 51fe9cd38d4d64b5fade8a899d5323da0e217d5c (diff) | |
union: Gracefully handle dangling symlinks in the input.
Fixes <http://bugs.gnu.org/26949>.
Reported by Pjotr Prins <pjotr.public12@thebird.nl>.
* guix/build/union.scm (file-is-directory?): Return #f when FILE does
not exist or is a dangling symlink.
(file=?): Pass #f as a second argument to 'stat'; return #f when both
ST1 or ST2 is #f.
* tests/profiles.scm (test-equalm): New macro.
("union vs. dangling symlink"): New test.
| -rw-r--r-- | .dir-locals.el | 1 | ||||
| -rw-r--r-- | guix/build/union.scm | 43 | ||||
| -rw-r--r-- | tests/profiles.scm | 29 |
3 files changed, 53 insertions, 20 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index 4aaeae95c92..04b58d2ce05 100644 --- a/.dir-locals.el +++ b/.dir-locals.el | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | (eval . (put 'call-with-prompt 'scheme-indent-function 1)) | 17 | (eval . (put 'call-with-prompt 'scheme-indent-function 1)) |
| 18 | (eval . (put 'test-assert 'scheme-indent-function 1)) | 18 | (eval . (put 'test-assert 'scheme-indent-function 1)) |
| 19 | (eval . (put 'test-assertm 'scheme-indent-function 1)) | 19 | (eval . (put 'test-assertm 'scheme-indent-function 1)) |
| 20 | (eval . (put 'test-equalm 'scheme-indent-function 1)) | ||
| 20 | (eval . (put 'test-equal 'scheme-indent-function 1)) | 21 | (eval . (put 'test-equal 'scheme-indent-function 1)) |
| 21 | (eval . (put 'test-eq 'scheme-indent-function 1)) | 22 | (eval . (put 'test-eq 'scheme-indent-function 1)) |
| 22 | (eval . (put 'call-with-input-string 'scheme-indent-function 1)) | 23 | (eval . (put 'call-with-input-string 'scheme-indent-function 1)) |
diff --git a/guix/build/union.scm b/guix/build/union.scm index a2ea72e1f59..18167fa3e3c 100644 --- a/guix/build/union.scm +++ b/guix/build/union.scm | |||
| @@ -47,31 +47,34 @@ | |||
| 47 | (loop (cons file files))))))) | 47 | (loop (cons file files))))))) |
| 48 | 48 | ||
| 49 | (define (file-is-directory? file) | 49 | (define (file-is-directory? file) |
| 50 | (eq? 'directory (stat:type (stat file)))) | 50 | (match (stat file #f) |
| 51 | (#f #f) ;maybe a dangling symlink | ||
| 52 | (st (eq? 'directory (stat:type st))))) | ||
| 51 | 53 | ||
| 52 | (define (file=? file1 file2) | 54 | (define (file=? file1 file2) |
| 53 | "Return #t if FILE1 and FILE2 are regular files and their contents are | 55 | "Return #t if FILE1 and FILE2 are regular files and their contents are |
| 54 | identical, #f otherwise." | 56 | identical, #f otherwise." |
| 55 | (let ((st1 (stat file1)) | 57 | (let ((st1 (stat file1 #f)) |
| 56 | (st2 (stat file2))) | 58 | (st2 (stat file2 #f))) |
| 57 | ;; When deduplication is enabled, identical files share the same inode. | 59 | ;; When deduplication is enabled, identical files share the same inode. |
| 58 | (or (= (stat:ino st1) (stat:ino st2)) | 60 | (and st1 st2 |
| 59 | (and (eq? (stat:type st1) 'regular) | 61 | (or (= (stat:ino st1) (stat:ino st2)) |
| 60 | (eq? (stat:type st2) 'regular) | 62 | (and (eq? (stat:type st1) 'regular) |
| 61 | (= (stat:size st1) (stat:size st2)) | 63 | (eq? (stat:type st2) 'regular) |
| 62 | (call-with-input-file file1 | 64 | (= (stat:size st1) (stat:size st2)) |
| 63 | (lambda (port1) | 65 | (call-with-input-file file1 |
| 64 | (call-with-input-file file2 | 66 | (lambda (port1) |
| 65 | (lambda (port2) | 67 | (call-with-input-file file2 |
| 66 | (define len 8192) | 68 | (lambda (port2) |
| 67 | (define buf1 (make-bytevector len)) | 69 | (define len 8192) |
| 68 | (define buf2 (make-bytevector len)) | 70 | (define buf1 (make-bytevector len)) |
| 69 | (let loop () | 71 | (define buf2 (make-bytevector len)) |
| 70 | (let ((n1 (get-bytevector-n! port1 buf1 0 len)) | 72 | (let loop () |
| 71 | (n2 (get-bytevector-n! port2 buf2 0 len))) | 73 | (let ((n1 (get-bytevector-n! port1 buf1 0 len)) |
| 72 | (and (equal? n1 n2) | 74 | (n2 (get-bytevector-n! port2 buf2 0 len))) |
| 73 | (or (eof-object? n1) | 75 | (and (equal? n1 n2) |
| 74 | (loop))))))))))))) | 76 | (or (eof-object? n1) |
| 77 | (loop)))))))))))))) | ||
| 75 | 78 | ||
| 76 | (define* (union-build output inputs | 79 | (define* (union-build output inputs |
| 77 | #:key (log-port (current-error-port)) | 80 | #:key (log-port (current-error-port)) |
diff --git a/tests/profiles.scm b/tests/profiles.scm index d0b1e14a865..093422792f4 100644 --- a/tests/profiles.scm +++ b/tests/profiles.scm | |||
| @@ -50,6 +50,12 @@ | |||
| 50 | (run-with-store %store exp | 50 | (run-with-store %store exp |
| 51 | #:guile-for-build (%guile-for-build)))) | 51 | #:guile-for-build (%guile-for-build)))) |
| 52 | 52 | ||
| 53 | (define-syntax-rule (test-equalm name value exp) | ||
| 54 | (test-equal name | ||
| 55 | value | ||
| 56 | (run-with-store %store exp | ||
| 57 | #:guile-for-build (%guile-for-build)))) | ||
| 58 | |||
| 53 | ;; Example manifest entries. | 59 | ;; Example manifest entries. |
| 54 | 60 | ||
| 55 | (define guile-1.8.8 | 61 | (define guile-1.8.8 |
| @@ -366,6 +372,29 @@ | |||
| 366 | get-string-all) | 372 | get-string-all) |
| 367 | "foo!")))))) | 373 | "foo!")))))) |
| 368 | 374 | ||
| 375 | (test-equalm "union vs. dangling symlink" ;<https://bugs.gnu.org/26949> | ||
| 376 | "does-not-exist" | ||
| 377 | (mlet* %store-monad | ||
| 378 | ((thing1 -> (dummy-package "dummy" | ||
| 379 | (build-system trivial-build-system) | ||
| 380 | (arguments | ||
| 381 | `(#:guile ,%bootstrap-guile | ||
| 382 | #:builder | ||
| 383 | (let ((out (assoc-ref %outputs "out"))) | ||
| 384 | (mkdir out) | ||
| 385 | (symlink "does-not-exist" | ||
| 386 | (string-append out "/dangling")) | ||
| 387 | #t))))) | ||
| 388 | (thing2 -> (package (inherit thing1) (name "dummy2"))) | ||
| 389 | (drv (profile-derivation (packages->manifest | ||
| 390 | (list thing1 thing2)) | ||
| 391 | #:hooks '() | ||
| 392 | #:locales? #f)) | ||
| 393 | (profile -> (derivation->output-path drv))) | ||
| 394 | (mbegin %store-monad | ||
| 395 | (built-derivations (list drv)) | ||
| 396 | (return (readlink (readlink (string-append profile "/dangling"))))))) | ||
| 397 | |||
| 369 | (test-end "profiles") | 398 | (test-end "profiles") |
| 370 | 399 | ||
| 371 | ;;; Local Variables: | 400 | ;;; Local Variables: |
