2021-09-19 14:44:34 -04:00
|
|
|
(define-library (tar)
|
|
|
|
(export tar-content-type
|
|
|
|
tar-content-type?
|
|
|
|
tar-entry-name
|
|
|
|
tar-entry-size
|
|
|
|
read-tar-entry
|
|
|
|
read-tar-entry-bytes
|
|
|
|
make-tar-padding
|
|
|
|
make-tar-eof
|
|
|
|
make-tar-header-for-regular-file)
|
|
|
|
(import (scheme base))
|
|
|
|
(begin
|
|
|
|
|
|
|
|
(define (align multiple value)
|
|
|
|
(truncate-remainder (- multiple (truncate-remainder value multiple))
|
|
|
|
multiple))
|
|
|
|
|
|
|
|
(define (bytevector-fold merge state bytes)
|
|
|
|
(let loop ((state state) (i 0))
|
|
|
|
(if (= i (bytevector-length bytes)) state
|
|
|
|
(loop (merge (bytevector-u8-ref bytes i) state) (+ i 1)))))
|
|
|
|
|
|
|
|
(define (bytevector-every? f bytes)
|
|
|
|
(let loop ((i 0))
|
|
|
|
(or (= i (bytevector-length bytes))
|
|
|
|
(and (f (bytevector-u8-ref bytes i))
|
|
|
|
(loop (+ i 1))))))
|
|
|
|
|
|
|
|
(define (read-exactly-n-bytes n)
|
2021-09-20 05:21:36 -04:00
|
|
|
(let* ((bytex (read-bytevector n))
|
|
|
|
(bytes (if (eof-object? bytex) (bytevector) bytex)))
|
|
|
|
(if (< (bytevector-length bytes) n)
|
|
|
|
(error "Short read / wanted / got" n (bytevector-length bytes))
|
|
|
|
bytes)))
|
2021-09-19 14:44:34 -04:00
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
(define tar-content-type "application/x-tar")
|
|
|
|
|
|
|
|
(define (tar-content-type? symbol)
|
|
|
|
(eq? symbol (string->symbol tar-content-type)))
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
(define (tar-limit-exceeded)
|
|
|
|
(error "tar limit exceeded"))
|
|
|
|
|
|
|
|
(define (tar-string nbytes str)
|
|
|
|
(let* ((bytes (string->utf8 str))
|
|
|
|
(room (- nbytes (bytevector-length bytes))))
|
|
|
|
(if (> room 0)
|
|
|
|
(bytevector-append bytes (make-bytevector room 0))
|
|
|
|
(tar-limit-exceeded))))
|
|
|
|
|
|
|
|
(define (tar-entry-string-ref entry offset len)
|
|
|
|
(let ((limit (let loop ((limit (+ offset len)))
|
|
|
|
(cond ((<= limit offset)
|
|
|
|
offset)
|
|
|
|
((zero? (bytevector-u8-ref entry (- limit 1)))
|
|
|
|
(loop (- limit 1)))
|
|
|
|
(else
|
|
|
|
limit)))))
|
|
|
|
(if (= limit (+ offset len))
|
|
|
|
(error "tar: string is not null terminated")
|
|
|
|
(utf8->string (bytevector-copy entry offset limit)))))
|
|
|
|
|
|
|
|
(define (tar-octal nbytes value)
|
|
|
|
(let* ((bytes (string->utf8 (number->string value 8)))
|
|
|
|
(room (- nbytes (bytevector-length bytes))))
|
|
|
|
(if (> room 0)
|
|
|
|
(bytevector-append (make-bytevector (- room 1)
|
|
|
|
(char->integer #\0))
|
|
|
|
bytes
|
|
|
|
(bytevector 0))
|
|
|
|
(tar-limit-exceeded))))
|
|
|
|
|
|
|
|
(define (tar-entry-octal-ref entry offset len)
|
|
|
|
(let loop ((offset offset) (len len) (value 0))
|
|
|
|
(if (<= len 0) value
|
|
|
|
(let ((dig0 (char->integer #\0))
|
|
|
|
(dig7 (char->integer #\7))
|
|
|
|
(byte (bytevector-u8-ref entry offset)))
|
|
|
|
(loop (+ offset 1) (- len 1)
|
|
|
|
(if (<= dig0 byte dig7)
|
|
|
|
(let ((digit (- byte dig0)))
|
|
|
|
(+ digit (* value 8)))
|
|
|
|
value))))))
|
|
|
|
|
|
|
|
(define (tar-entry-name entry)
|
|
|
|
(tar-entry-string-ref entry 0 100))
|
|
|
|
|
|
|
|
(define (tar-entry-size entry)
|
|
|
|
(tar-entry-octal-ref entry 124 12))
|
|
|
|
|
|
|
|
(define (read-tar-entry)
|
|
|
|
(let ((entry (read-exactly-n-bytes 512)))
|
|
|
|
(if (bytevector-every? zero? entry) (eof-object) entry)))
|
|
|
|
|
|
|
|
(define (read-tar-entry-bytes entry)
|
|
|
|
(let* ((nbyte (tar-entry-size entry))
|
|
|
|
(bytes (read-exactly-n-bytes nbyte))
|
|
|
|
(nulls (read-exactly-n-bytes (align 512 nbyte))))
|
|
|
|
bytes))
|
|
|
|
|
|
|
|
(define (make-tar-padding bytes)
|
|
|
|
(make-bytevector (align 512 (bytevector-length bytes)) 0))
|
|
|
|
|
|
|
|
(define (make-tar-eof)
|
|
|
|
(make-bytevector (* 512 2) 0))
|
|
|
|
|
|
|
|
(define (make-tar-header-for-regular-file filename bytes)
|
|
|
|
(let* ((before-checksum
|
|
|
|
(bytevector-append
|
|
|
|
(tar-string 100 filename)
|
|
|
|
(tar-octal 8 #x444)
|
|
|
|
(tar-octal 8 0)
|
|
|
|
(tar-octal 8 0)
|
|
|
|
(tar-octal 12 (bytevector-length bytes))
|
|
|
|
(tar-octal 12 0)))
|
|
|
|
(after-checksum
|
|
|
|
(bytevector-append
|
|
|
|
(bytevector (char->integer #\space))
|
|
|
|
(bytevector (char->integer #\0))
|
|
|
|
(tar-string 100 "")
|
|
|
|
(tar-string 6 "ustar")
|
|
|
|
(string->utf8 "00")
|
|
|
|
(tar-string 32 "root")
|
|
|
|
(tar-string 32 "root")
|
|
|
|
(tar-octal 8 0)
|
|
|
|
(tar-octal 8 0)
|
|
|
|
(tar-string 155 "")
|
|
|
|
(make-bytevector 12 0)))
|
|
|
|
(blank-checksum
|
|
|
|
(make-bytevector 7 (char->integer #\space)))
|
|
|
|
(checksum
|
|
|
|
(truncate-remainder (+ (bytevector-fold + 0 before-checksum)
|
|
|
|
(bytevector-fold + 0 blank-checksum)
|
|
|
|
(bytevector-fold + 0 after-checksum))
|
|
|
|
(expt 8 6))))
|
|
|
|
(bytevector-append before-checksum
|
|
|
|
(tar-octal 7 checksum)
|
|
|
|
after-checksum)))))
|