#!/usr/bin/newlisp

; Utility to copy from a previous overlay file onto a current
; fusefile. This would typically be done so as to "merge down" an
; overlay in an old fusefile overlay stack after having retired that
; stack. (There is an illustration example at the end of this file)
;
; Arguments: <fusefile> <overlay>
;
; Technically this uitility merely "replays" the writes (in order of
; increasing position rather than their actual time order) that have
; been collated into an overlay file. It writes this into a file or
; fusefile that represents what the basis was when the writes
; happened.

; (die ...) - function to drop a note to stderr and exit with 1.
(define (die)
  (write-line 2 (join (map string (args)) " "))
  (exit 1))

; (read-uint64 FD N) - function to read N consequtive unsigned long from
; file descriptor FD. Return them as a list in order
(define (read-uint64 FD N)
  (let ((B (* N 8)) (BUFFER nil) (HEAD "") (OUT '()))
    ;; B = number of bytes to read
    ;; BUFFER = input buffer symbol
    ;; HEAD = prefix of bytes read but not processed
    ;; OUT = list of unsigned long to return
    ;;
    ;; Note that (read..) might return fewer bytes than asked for so
    ;; it needs a loop. 
    (while (and (> B) (> (read FD BUFFER B)))
      (dec B (length BUFFER)) 
      (extend HEAD BUFFER)
      (let ((I (/ (length HEAD) 8)))
        (when (> I)
          (extend OUT (unpack (dup "Lu" ) HEAD))
          (setf HEAD ((* 8 I) HEAD)))))
    (when (> B) (die "Cannot read" N "unsigned long."))
    OUT))

; (copy-data AT SIZE) - copy the data block of SIZE bytes at position
; AT from OL.fd to FF.fd.
(define (copy-data AT SIZE) ; FUSEFILE FF.fd OVERLAY OL.fd
  (let ((BUFFER nil) (N 0))
    ;; BUFEER = transfer buffer symbol
    ;; N = number of bytes written upon each (write...)
    (when (null? (seek FF.fd AT)) (die "Cannot seek" FUSEFILE AT))
    (when (null? (seek OL.fd AT)) (die "Cannot seek" OVERLAY AT))
    (while (> SIZE)
      (when (<= (read OL.fd BUFFER SIZE))
        (die "Failed reading" SIZE "bytes"))
      (dec SIZE (length BUFFER))
      (while (and (> (length BUFFER))
                  (setf N (write FF.fd BUFFER (length BUFFER))))
        (setf BUFFER (N BUFFER)))
      (when (> (length BUFFER)) (die "Failed writing" AT SIZE ))
      )))

;=== Main program

; Require 2 command line arguments, afterinterpreter and script file.
(when (!= (length (main-args)) 4)
  (die "Requires arguments: fusefile overlay"))

; Set up globals
(setf
 FUSEFILE (main-args -2)
 FF.fd (if (open FUSEFILE "u") $it (die "Cannot open" FUSEFILE))
 FF.end (file-info FUSEFILE 0)
 OVERLAY (main-args -1)
 OL.fd (if (open OVERLAY "r") $it (die "Cannot open" OVERLAY))
 OL.end (file-info OVERLAY 0)
 OL.N (if (seek OL.fd FF.end)
          (if (read-uint64 OL.fd 1) ($it 0)
            (die "Cannot read" OVERLAY "table count."))
        (die "Cannot seek" OVERLY "to" FF.end))
 )

; Confirm expected file size for overlay
(unless (= OL.end (+ FF.end 8 (* OL.N 16)))
  (die OVERLAY "should be" OL.end "bytes"))

; Load the overlay table and copy data according to its entries
(dolist (ENTRY (explode
                (if (read-uint64 OL.fd (* 2 OL.N)) $it
                  (die "Cannot read overlay table from" OVERLAY))
                2))
  ;; ENTRY = (begin end) for data block
  (println (format "copy %s/%d:%d" (cons OVERLAY ENTRY)))
  (copy-data (ENTRY 0) (- (ENTRY 1) (ENTRY 0))))

(exit 0)
"dumpoverlay.lsp"

; E.g., consider a fusefile stack D:A:B:C with overlays A:B:C over D.
; That stack would have been built in a succession of first using
; stack D:A where changes are collated into overlay A, as in the
; following setup:
;
; $ fusefile E -overlay:A D
; ... writes to E (= D:A) goes into A
; $ fusermount -u E
;
; Note that the "D" part might in reality be a more complex
; composition of several file fragments, but in these examples we
; refer to it simply as "D".
;
; Continuing the example, the new stack D:A:B is used for collating
; further changes into B, as in the following setup:
;
; $ fusefile E -overlay:A:B D
; ... writes to E (= D:A:B) actually goes into B
; $ fusermount -u E
;
; Later again, the stack D:A:B:C is used for collating changes into C,
; as in the following setup:
;
; $ fusefile E -overlay:A:B:C D
; ... writes to E (= D:A:B:C) actually goes into C
; ...
; $ fusermount -u D
;
; At that point, one may decide to merge down the overlay C onto a
; D:A:B fusefile and thereby add the C changes over B.
;
; $ fusefile E -overlay:A:B D
; $ merge-overlay E C
; $ rm C
; $ fusermount -u E
; 
; Or alternatively, one may decide to merge down the B changes without
; C onto a D:A fusefile and thereby add B to A.
;
; $ fusefile E -overlay:A D
; $ merge-overlay E B
; $ rm B
; $ fusermount -u E
;
; In the latter case the updated overlay A includes the writes
; collated in B and thus now the stack D:A:C would be the same as the
; previous stack D:A:B:C.
;
; End of example.
