#!/usr/bin/newlisp
-;
-; Copy from a fusefile overlay file onto a fusefile. This may be done
-; so as to "merge down" an overlay in an overlay stack. Eg, assume the
-; original fusefile had overlay stack A:B:C then C could be merged
-; onto an A:B stack, or B merged onto A; the latter allowing a
-; subsequent A:C stack corresponding to previous A:B:C.
+; 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.
-(map set '(FUSEFILE OVERLAY) (-2 (main-args)))
-
-(setf FF.fd (open FUSEFILE "u") OL.fd (open OVERLAY "r"))
-
+; (die ...) - function to drop a note to stderr and exit with 1.
(define (die)
(write-line 2 (join (map string (args)) " "))
(exit 1))
-; Read N unsigned 64-bit integers from OL.fd, returned as a list of them.
-(define (rd-uint64 FD N)
+; (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))
+ (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 "table entries"))
+ (when (> B) (die "Cannot read" N "unsigned long."))
OUT))
-(define (copy-data AT SIZE)
+; (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"))
+ (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))))
(when (> (length BUFFER)) (die "Failed writing" AT SIZE ))
)))
-(when (< FF.fd) (die "Cannot open" FUSEFILE))
-(when (< OL.fd) (die "Cannot open" OVERLAY))
-
-(when (null? (seek OL.fd (setf P (file-info FUSEFILE 0))))
- (die "Seek error:" P))
+;=== Main program
-(setf N (rd-uint64 OL.fd 1))
-(when (null? N) (die "Cannot read" OVERLAY "table count."))
-(setf N (N 0))
+; Require 2 command line arguments, afterinterpreter and script file.
+(when (!= (length (main-args)) 4)
+ (die "Requires arguments: fusefile overlay"))
-; Check size
-(when (!= (+ P 8 (* N 16)) (file-info OVERLAY 0))
- (die "Wrong size for " OVERLAY "which should be" (+ P 8 (* N 16))))
+; 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))
+ )
-(setf TABLE (rd-uint64 OL.fd (* 2 N)))
-(when (null? TABLE) (die "Cannot read" OVERLAY "table"))
+; Confirm expected file size for overlay
+(unless (= OL.end (+ FF.end 8 (* OL.N 16)))
+ (die OVERLAY "should be" OL.end "bytes"))
-(dolist (ENTRY (explode TABLE 2))
+; 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))))
-"dumpoverlay.lsp"
(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.