serialization: Check for EOF and incomplete input conditions.
Fixes <http://bugs.gnu.org/19756>. Reported by <sleep_walker@suse.cz>. * guix/serialization.scm (currently-restored-file): New variable. (get-bytevector-n*): New procedure. (read-int, read-long-long, read-string, read-latin1-string, read-contents): Use it instead of 'get-bytevector-n'. (restore-file): Parameterize 'currently-restored-file' and set it. * tests/nar.scm ("restore-file with incomplete input"): New test.master
parent
cbc538fe69
commit
46b8aadbd6
|
@ -56,13 +56,32 @@
|
||||||
|
|
||||||
;; Similar to serialize.cc in Nix.
|
;; Similar to serialize.cc in Nix.
|
||||||
|
|
||||||
|
(define-condition-type &nar-error &error ; XXX: inherit from &nix-error ?
|
||||||
|
nar-error?
|
||||||
|
(file nar-error-file) ; file we were restoring, or #f
|
||||||
|
(port nar-error-port)) ; port from which we read
|
||||||
|
|
||||||
|
(define currently-restored-file
|
||||||
|
;; Name of the file being restored. Used internally for error reporting.
|
||||||
|
(make-parameter #f))
|
||||||
|
|
||||||
|
|
||||||
|
(define (get-bytevector-n* port count)
|
||||||
|
(let ((bv (get-bytevector-n port count)))
|
||||||
|
(when (or (eof-object? bv)
|
||||||
|
(< (bytevector-length bv) count))
|
||||||
|
(raise (condition (&nar-error
|
||||||
|
(file (currently-restored-file))
|
||||||
|
(port port)))))
|
||||||
|
bv))
|
||||||
|
|
||||||
(define (write-int n p)
|
(define (write-int n p)
|
||||||
(let ((b (make-bytevector 8 0)))
|
(let ((b (make-bytevector 8 0)))
|
||||||
(bytevector-u32-set! b 0 n (endianness little))
|
(bytevector-u32-set! b 0 n (endianness little))
|
||||||
(put-bytevector p b)))
|
(put-bytevector p b)))
|
||||||
|
|
||||||
(define (read-int p)
|
(define (read-int p)
|
||||||
(let ((b (get-bytevector-n p 8)))
|
(let ((b (get-bytevector-n* p 8)))
|
||||||
(bytevector-u32-ref b 0 (endianness little))))
|
(bytevector-u32-ref b 0 (endianness little))))
|
||||||
|
|
||||||
(define (write-long-long n p)
|
(define (write-long-long n p)
|
||||||
|
@ -71,7 +90,7 @@
|
||||||
(put-bytevector p b)))
|
(put-bytevector p b)))
|
||||||
|
|
||||||
(define (read-long-long p)
|
(define (read-long-long p)
|
||||||
(let ((b (get-bytevector-n p 8)))
|
(let ((b (get-bytevector-n* p 8)))
|
||||||
(bytevector-u64-ref b 0 (endianness little))))
|
(bytevector-u64-ref b 0 (endianness little))))
|
||||||
|
|
||||||
(define write-padding
|
(define write-padding
|
||||||
|
@ -93,10 +112,10 @@
|
||||||
(define (read-string p)
|
(define (read-string p)
|
||||||
(let* ((len (read-int p))
|
(let* ((len (read-int p))
|
||||||
(m (modulo len 8))
|
(m (modulo len 8))
|
||||||
(bv (get-bytevector-n p len))
|
(bv (get-bytevector-n* p len))
|
||||||
(str (utf8->string bv)))
|
(str (utf8->string bv)))
|
||||||
(or (zero? m)
|
(or (zero? m)
|
||||||
(get-bytevector-n p (- 8 m)))
|
(get-bytevector-n* p (- 8 m)))
|
||||||
str))
|
str))
|
||||||
|
|
||||||
(define (read-latin1-string p)
|
(define (read-latin1-string p)
|
||||||
|
@ -105,9 +124,9 @@
|
||||||
;; Note: do not use 'get-string-n' to work around Guile bug
|
;; Note: do not use 'get-string-n' to work around Guile bug
|
||||||
;; <http://bugs.gnu.org/19621>. See <http://bugs.gnu.org/19610> for
|
;; <http://bugs.gnu.org/19621>. See <http://bugs.gnu.org/19610> for
|
||||||
;; a discussion.
|
;; a discussion.
|
||||||
(str (get-bytevector-n p len)))
|
(str (get-bytevector-n* p len)))
|
||||||
(or (zero? m)
|
(or (zero? m)
|
||||||
(get-bytevector-n p (- 8 m)))
|
(get-bytevector-n* p (- 8 m)))
|
||||||
|
|
||||||
;; XXX: Rewrite using (ice-9 iconv) when the minimum requirement is
|
;; XXX: Rewrite using (ice-9 iconv) when the minimum requirement is
|
||||||
;; upgraded to Guile >= 2.0.9.
|
;; upgraded to Guile >= 2.0.9.
|
||||||
|
@ -143,11 +162,6 @@
|
||||||
(define read-store-path-list read-string-list)
|
(define read-store-path-list read-string-list)
|
||||||
|
|
||||||
|
|
||||||
(define-condition-type &nar-error &error ; XXX: inherit from &nix-error ?
|
|
||||||
nar-error?
|
|
||||||
(file nar-error-file) ; file we were restoring, or #f
|
|
||||||
(port nar-error-port)) ; port from which we read
|
|
||||||
|
|
||||||
(define-condition-type &nar-read-error &nar-error
|
(define-condition-type &nar-read-error &nar-error
|
||||||
nar-read-error?
|
nar-read-error?
|
||||||
(token nar-read-error-token)) ; faulty token, or #f
|
(token nar-read-error-token)) ; faulty token, or #f
|
||||||
|
@ -222,7 +236,7 @@ the size in bytes."
|
||||||
(chmod out #o755))
|
(chmod out #o755))
|
||||||
(let ((m (modulo size 8)))
|
(let ((m (modulo size 8)))
|
||||||
(unless (zero? m)
|
(unless (zero? m)
|
||||||
(get-bytevector-n in (- 8 m))))
|
(get-bytevector-n* in (- 8 m))))
|
||||||
size))
|
size))
|
||||||
|
|
||||||
(define %archive-version-1
|
(define %archive-version-1
|
||||||
|
@ -286,68 +300,71 @@ sub-directories of FILE as needed."
|
||||||
(define (restore-file port file)
|
(define (restore-file port file)
|
||||||
"Read a file (possibly a directory structure) in Nar format from PORT.
|
"Read a file (possibly a directory structure) in Nar format from PORT.
|
||||||
Restore it as FILE."
|
Restore it as FILE."
|
||||||
(let ((signature (read-string port)))
|
(parameterize ((currently-restored-file file))
|
||||||
(unless (equal? signature %archive-version-1)
|
(let ((signature (read-string port)))
|
||||||
(raise
|
(unless (equal? signature %archive-version-1)
|
||||||
(condition (&message (message "invalid nar signature"))
|
(raise
|
||||||
(&nar-read-error (port port)
|
(condition (&message (message "invalid nar signature"))
|
||||||
(token signature)
|
(&nar-read-error (port port)
|
||||||
(file #f))))))
|
(token signature)
|
||||||
|
(file #f))))))
|
||||||
|
|
||||||
(let restore ((file file))
|
(let restore ((file file))
|
||||||
(define (read-eof-marker)
|
(define (read-eof-marker)
|
||||||
(match (read-string port)
|
(match (read-string port)
|
||||||
(")" #t)
|
(")" #t)
|
||||||
(x (raise
|
(x (raise
|
||||||
(condition
|
(condition
|
||||||
(&message (message "invalid nar end-of-file marker"))
|
(&message (message "invalid nar end-of-file marker"))
|
||||||
(&nar-read-error (port port) (file file) (token x)))))))
|
(&nar-read-error (port port) (file file) (token x)))))))
|
||||||
|
|
||||||
(match (list (read-string port) (read-string port) (read-string port))
|
(currently-restored-file file)
|
||||||
(("(" "type" "regular")
|
|
||||||
(call-with-output-file file (cut read-contents port <>))
|
(match (list (read-string port) (read-string port) (read-string port))
|
||||||
(read-eof-marker))
|
(("(" "type" "regular")
|
||||||
(("(" "type" "symlink")
|
(call-with-output-file file (cut read-contents port <>))
|
||||||
(match (list (read-string port) (read-string port))
|
(read-eof-marker))
|
||||||
(("target" target)
|
(("(" "type" "symlink")
|
||||||
(symlink target file)
|
(match (list (read-string port) (read-string port))
|
||||||
(read-eof-marker))
|
(("target" target)
|
||||||
(x (raise
|
(symlink target file)
|
||||||
(condition
|
(read-eof-marker))
|
||||||
(&message (message "invalid symlink tokens"))
|
(x (raise
|
||||||
(&nar-read-error (port port) (file file) (token x)))))))
|
|
||||||
(("(" "type" "directory")
|
|
||||||
(let ((dir file))
|
|
||||||
(mkdir dir)
|
|
||||||
(let loop ((prefix (read-string port)))
|
|
||||||
(match prefix
|
|
||||||
("entry"
|
|
||||||
(match (list (read-string port)
|
|
||||||
(read-string port) (read-string port)
|
|
||||||
(read-string port))
|
|
||||||
(("(" "name" file "node")
|
|
||||||
(restore (string-append dir "/" file))
|
|
||||||
(match (read-string port)
|
|
||||||
(")" #t)
|
|
||||||
(x
|
|
||||||
(raise
|
|
||||||
(condition
|
|
||||||
(&message
|
|
||||||
(message "unexpected directory entry termination"))
|
|
||||||
(&nar-read-error (port port)
|
|
||||||
(file file)
|
|
||||||
(token x))))))
|
|
||||||
(loop (read-string port)))))
|
|
||||||
(")" #t) ; done with DIR
|
|
||||||
(x
|
|
||||||
(raise
|
|
||||||
(condition
|
(condition
|
||||||
(&message (message "unexpected directory inter-entry marker"))
|
(&message (message "invalid symlink tokens"))
|
||||||
(&nar-read-error (port port) (file file) (token x)))))))))
|
(&nar-read-error (port port) (file file) (token x)))))))
|
||||||
(x
|
(("(" "type" "directory")
|
||||||
(raise
|
(let ((dir file))
|
||||||
(condition
|
(mkdir dir)
|
||||||
(&message (message "unsupported nar entry type"))
|
(let loop ((prefix (read-string port)))
|
||||||
(&nar-read-error (port port) (file file) (token x))))))))
|
(match prefix
|
||||||
|
("entry"
|
||||||
|
(match (list (read-string port)
|
||||||
|
(read-string port) (read-string port)
|
||||||
|
(read-string port))
|
||||||
|
(("(" "name" file "node")
|
||||||
|
(restore (string-append dir "/" file))
|
||||||
|
(match (read-string port)
|
||||||
|
(")" #t)
|
||||||
|
(x
|
||||||
|
(raise
|
||||||
|
(condition
|
||||||
|
(&message
|
||||||
|
(message "unexpected directory entry termination"))
|
||||||
|
(&nar-read-error (port port)
|
||||||
|
(file file)
|
||||||
|
(token x))))))
|
||||||
|
(loop (read-string port)))))
|
||||||
|
(")" #t) ; done with DIR
|
||||||
|
(x
|
||||||
|
(raise
|
||||||
|
(condition
|
||||||
|
(&message (message "unexpected directory inter-entry marker"))
|
||||||
|
(&nar-read-error (port port) (file file) (token x)))))))))
|
||||||
|
(x
|
||||||
|
(raise
|
||||||
|
(condition
|
||||||
|
(&message (message "unsupported nar entry type"))
|
||||||
|
(&nar-read-error (port port) (file file) (token x)))))))))
|
||||||
|
|
||||||
;;; serialization.scm ends here
|
;;; serialization.scm ends here
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
|
;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -201,6 +201,15 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(rm-rf input)))))
|
(rm-rf input)))))
|
||||||
|
|
||||||
|
(test-equal "restore-file with incomplete input"
|
||||||
|
(string-append %test-dir "/foo")
|
||||||
|
(let ((port (open-bytevector-input-port #vu8(1 2 3))))
|
||||||
|
(guard (c ((nar-error? c)
|
||||||
|
(and (eq? port (nar-error-port c))
|
||||||
|
(nar-error-file c))))
|
||||||
|
(restore-file port (string-append %test-dir "/foo"))
|
||||||
|
#f)))
|
||||||
|
|
||||||
(test-assert "write-file + restore-file"
|
(test-assert "write-file + restore-file"
|
||||||
(let* ((input (string-append (dirname (search-path %load-path "guix.scm"))
|
(let* ((input (string-append (dirname (search-path %load-path "guix.scm"))
|
||||||
"/guix"))
|
"/guix"))
|
||||||
|
|
Reference in New Issue