serialization: Add 'fold-archive'.
* guix/serialization.scm (read-contents): Remove. (read-file-type, fold-archive): New procedures. (restore-file): Rewrite in terms of 'fold-archive'. * tests/nar.scm ("write-file-tree + fold-archive") ("write-file-tree + fold-archive, flat file"): New tests.master
parent
55e21617d6
commit
12c1afcdbd
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
write-file
|
write-file
|
||||||
write-file-tree
|
write-file-tree
|
||||||
|
fold-archive
|
||||||
restore-file))
|
restore-file))
|
||||||
|
|
||||||
;;; Comment:
|
;;; Comment:
|
||||||
|
@ -226,38 +227,25 @@ substitute invalid byte sequences with question marks. This is a
|
||||||
(dump input output size))
|
(dump input output size))
|
||||||
(write-padding size output))
|
(write-padding size output))
|
||||||
|
|
||||||
(define (read-contents in out)
|
(define (read-file-type port)
|
||||||
"Read the contents of a file from the Nar at IN, write it to OUT, and return
|
"Read the file type tag from PORT, and return either 'regular or
|
||||||
the size in bytes."
|
'executable."
|
||||||
(define executable?
|
(match (read-string port)
|
||||||
(match (read-string in)
|
("contents"
|
||||||
("contents"
|
'regular)
|
||||||
#f)
|
("executable"
|
||||||
("executable"
|
(match (list (read-string port) (read-string port))
|
||||||
(match (list (read-string in) (read-string in))
|
(("" "contents") 'executable)
|
||||||
(("" "contents") #t)
|
(x (raise
|
||||||
(x (raise
|
(condition (&message
|
||||||
(condition (&message
|
(message "unexpected executable file marker"))
|
||||||
(message "unexpected executable file marker"))
|
(&nar-read-error (port port)
|
||||||
(&nar-read-error (port in)
|
(file #f)
|
||||||
(file #f)
|
(token x)))))))
|
||||||
(token x))))))
|
(x
|
||||||
#t)
|
(raise
|
||||||
(x
|
(condition (&message (message "unsupported nar file type"))
|
||||||
(raise
|
(&nar-read-error (port port) (file #f) (token x)))))))
|
||||||
(condition (&message (message "unsupported nar file type"))
|
|
||||||
(&nar-read-error (port in) (file #f) (token x)))))))
|
|
||||||
|
|
||||||
(let ((size (read-long-long in)))
|
|
||||||
;; Note: `sendfile' cannot be used here because of port buffering on IN.
|
|
||||||
(dump in out size)
|
|
||||||
|
|
||||||
(when executable?
|
|
||||||
(chmod out #o755))
|
|
||||||
(let ((m (modulo size 8)))
|
|
||||||
(unless (zero? m)
|
|
||||||
(get-bytevector-n* in (- 8 m))))
|
|
||||||
size))
|
|
||||||
|
|
||||||
(define %archive-version-1
|
(define %archive-version-1
|
||||||
;; Magic cookie for Nix archives.
|
;; Magic cookie for Nix archives.
|
||||||
|
@ -383,9 +371,14 @@ which case you can use 'identity'."
|
||||||
(define port-conversion-strategy
|
(define port-conversion-strategy
|
||||||
(fluid->parameter %default-port-conversion-strategy))
|
(fluid->parameter %default-port-conversion-strategy))
|
||||||
|
|
||||||
(define (restore-file port file)
|
(define (fold-archive proc seed 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. Call
|
||||||
Restore it as FILE."
|
PROC on each file or directory read from PORT using:
|
||||||
|
|
||||||
|
(PROC FILE TYPE CONTENTS RESULT)
|
||||||
|
|
||||||
|
using SEED as the first RESULT. TYPE is a symbol like 'regular, and CONTENTS
|
||||||
|
depends on TYPE."
|
||||||
(parameterize ((currently-restored-file file)
|
(parameterize ((currently-restored-file file)
|
||||||
|
|
||||||
;; Error out if we can convert file names to the current
|
;; Error out if we can convert file names to the current
|
||||||
|
@ -401,7 +394,8 @@ Restore it as FILE."
|
||||||
(token signature)
|
(token signature)
|
||||||
(file #f))))))
|
(file #f))))))
|
||||||
|
|
||||||
(let restore ((file file))
|
(let read ((file file)
|
||||||
|
(result seed))
|
||||||
(define (read-eof-marker)
|
(define (read-eof-marker)
|
||||||
(match (read-string port)
|
(match (read-string port)
|
||||||
(")" #t)
|
(")" #t)
|
||||||
|
@ -414,40 +408,49 @@ Restore it as FILE."
|
||||||
|
|
||||||
(match (list (read-string port) (read-string port) (read-string port))
|
(match (list (read-string port) (read-string port) (read-string port))
|
||||||
(("(" "type" "regular")
|
(("(" "type" "regular")
|
||||||
(call-with-output-file file (cut read-contents port <>))
|
(let* ((type (read-file-type port))
|
||||||
(read-eof-marker))
|
(size (read-long-long port))
|
||||||
|
|
||||||
|
;; The caller must read exactly SIZE bytes from PORT.
|
||||||
|
(result (proc file type `(,port . ,size) result)))
|
||||||
|
(let ((m (modulo size 8)))
|
||||||
|
(unless (zero? m)
|
||||||
|
(get-bytevector-n* port (- 8 m))))
|
||||||
|
(read-eof-marker)
|
||||||
|
result))
|
||||||
(("(" "type" "symlink")
|
(("(" "type" "symlink")
|
||||||
(match (list (read-string port) (read-string port))
|
(match (list (read-string port) (read-string port))
|
||||||
(("target" target)
|
(("target" target)
|
||||||
(symlink target file)
|
(let ((result (proc file 'symlink target result)))
|
||||||
(read-eof-marker))
|
(read-eof-marker)
|
||||||
|
result))
|
||||||
(x (raise
|
(x (raise
|
||||||
(condition
|
(condition
|
||||||
(&message (message "invalid symlink tokens"))
|
(&message (message "invalid symlink tokens"))
|
||||||
(&nar-read-error (port port) (file file) (token x)))))))
|
(&nar-read-error (port port) (file file) (token x)))))))
|
||||||
(("(" "type" "directory")
|
(("(" "type" "directory")
|
||||||
(let ((dir file))
|
(let ((dir file))
|
||||||
(mkdir dir)
|
(let loop ((prefix (read-string port))
|
||||||
(let loop ((prefix (read-string port)))
|
(result (proc file 'directory #f result)))
|
||||||
(match prefix
|
(match prefix
|
||||||
("entry"
|
("entry"
|
||||||
(match (list (read-string port)
|
(match (list (read-string port)
|
||||||
(read-string port) (read-string port)
|
(read-string port) (read-string port)
|
||||||
(read-string port))
|
(read-string port))
|
||||||
(("(" "name" file "node")
|
(("(" "name" file "node")
|
||||||
(restore (string-append dir "/" file))
|
(let ((result (read (string-append dir "/" file) result)))
|
||||||
(match (read-string port)
|
(match (read-string port)
|
||||||
(")" #t)
|
(")" #f)
|
||||||
(x
|
(x
|
||||||
(raise
|
(raise
|
||||||
(condition
|
(condition
|
||||||
(&message
|
(&message
|
||||||
(message "unexpected directory entry termination"))
|
(message "unexpected directory entry termination"))
|
||||||
(&nar-read-error (port port)
|
(&nar-read-error (port port)
|
||||||
(file file)
|
(file file)
|
||||||
(token x))))))
|
(token x))))))
|
||||||
(loop (read-string port)))))
|
(loop (read-string port) result)))))
|
||||||
(")" #t) ; done with DIR
|
(")" result) ;done with DIR
|
||||||
(x
|
(x
|
||||||
(raise
|
(raise
|
||||||
(condition
|
(condition
|
||||||
|
@ -459,6 +462,27 @@ Restore it as FILE."
|
||||||
(&message (message "unsupported nar entry type"))
|
(&message (message "unsupported nar entry type"))
|
||||||
(&nar-read-error (port port) (file file) (token x)))))))))
|
(&nar-read-error (port port) (file file) (token x)))))))))
|
||||||
|
|
||||||
|
(define (restore-file port file)
|
||||||
|
"Read a file (possibly a directory structure) in Nar format from PORT.
|
||||||
|
Restore it as FILE."
|
||||||
|
(fold-archive (lambda (file type content result)
|
||||||
|
(match type
|
||||||
|
('directory
|
||||||
|
(mkdir file))
|
||||||
|
('symlink
|
||||||
|
(symlink content file))
|
||||||
|
((or 'regular 'executable)
|
||||||
|
(match content
|
||||||
|
((input . size)
|
||||||
|
(call-with-output-file file
|
||||||
|
(lambda (output)
|
||||||
|
(dump input output size)
|
||||||
|
(when (eq? type 'executable)
|
||||||
|
(chmod output #o755)))))))))
|
||||||
|
#t
|
||||||
|
port
|
||||||
|
file))
|
||||||
|
|
||||||
;;; Local Variables:
|
;;; Local Variables:
|
||||||
;;; eval: (put 'call-with-binary-input-file 'scheme-indent-function 1)
|
;;; eval: (put 'call-with-binary-input-file 'scheme-indent-function 1)
|
||||||
;;; End:
|
;;; End:
|
||||||
|
|
|
@ -214,6 +214,80 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(false-if-exception (rm-rf %test-dir))))))
|
(false-if-exception (rm-rf %test-dir))))))
|
||||||
|
|
||||||
|
(test-equal "write-file-tree + fold-archive"
|
||||||
|
'(("R" directory #f)
|
||||||
|
("R/dir" directory #f)
|
||||||
|
("R/dir/exe" executable "1234")
|
||||||
|
("R/foo" regular "abcdefg")
|
||||||
|
("R/lnk" symlink "foo"))
|
||||||
|
|
||||||
|
(let ()
|
||||||
|
(define-values (port get-bytevector)
|
||||||
|
(open-bytevector-output-port))
|
||||||
|
(write-file-tree "root" port
|
||||||
|
#:file-type+size
|
||||||
|
(match-lambda
|
||||||
|
("root"
|
||||||
|
(values 'directory 0))
|
||||||
|
("root/foo"
|
||||||
|
(values 'regular 7))
|
||||||
|
("root/lnk"
|
||||||
|
(values 'symlink 0))
|
||||||
|
("root/dir"
|
||||||
|
(values 'directory 0))
|
||||||
|
("root/dir/exe"
|
||||||
|
(values 'executable 4)))
|
||||||
|
#:file-port
|
||||||
|
(match-lambda
|
||||||
|
("root/foo" (open-input-string "abcdefg"))
|
||||||
|
("root/dir/exe" (open-input-string "1234")))
|
||||||
|
#:symlink-target
|
||||||
|
(match-lambda
|
||||||
|
("root/lnk" "foo"))
|
||||||
|
#:directory-entries
|
||||||
|
(match-lambda
|
||||||
|
("root" '("foo" "dir" "lnk"))
|
||||||
|
("root/dir" '("exe"))))
|
||||||
|
(close-port port)
|
||||||
|
|
||||||
|
(reverse
|
||||||
|
(fold-archive (lambda (file type contents result)
|
||||||
|
(let ((contents (if (memq type '(regular executable))
|
||||||
|
(utf8->string
|
||||||
|
(get-bytevector-n (car contents)
|
||||||
|
(cdr contents)))
|
||||||
|
contents)))
|
||||||
|
(cons `(,file ,type ,contents)
|
||||||
|
result)))
|
||||||
|
'()
|
||||||
|
(open-bytevector-input-port (get-bytevector))
|
||||||
|
"R"))))
|
||||||
|
|
||||||
|
(test-equal "write-file-tree + fold-archive, flat file"
|
||||||
|
'(("R" regular "abcdefg"))
|
||||||
|
|
||||||
|
(let ()
|
||||||
|
(define-values (port get-bytevector)
|
||||||
|
(open-bytevector-output-port))
|
||||||
|
(write-file-tree "root" port
|
||||||
|
#:file-type+size
|
||||||
|
(match-lambda
|
||||||
|
("root" (values 'regular 7)))
|
||||||
|
#:file-port
|
||||||
|
(match-lambda
|
||||||
|
("root" (open-input-string "abcdefg"))))
|
||||||
|
(close-port port)
|
||||||
|
|
||||||
|
(reverse
|
||||||
|
(fold-archive (lambda (file type contents result)
|
||||||
|
(let ((contents (utf8->string
|
||||||
|
(get-bytevector-n (car contents)
|
||||||
|
(cdr contents)))))
|
||||||
|
(cons `(,file ,type ,contents) result)))
|
||||||
|
'()
|
||||||
|
(open-bytevector-input-port (get-bytevector))
|
||||||
|
"R"))))
|
||||||
|
|
||||||
(test-assert "write-file supports non-file output ports"
|
(test-assert "write-file supports non-file output ports"
|
||||||
(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