Corrected the "-dump" report of fragment start position.
[rrq/fusefile.git] / merge-overlay.lsp
1 #!/usr/bin/newlisp
2
3 ; Utility to copy from a previous overlay file onto a current
4 ; fusefile. This would typically be done so as to "merge down" an
5 ; overlay in an old fusefile overlay stack after having retired that
6 ; stack. (There is an illustration example at the end of this file)
7 ;
8 ; Arguments: <fusefile> <overlay>
9 ;
10 ; Technically this uitility merely "replays" the writes (in order of
11 ; increasing position rather than their actual time order) that have
12 ; been collated into an overlay file. It writes this into a file or
13 ; fusefile that represents what the basis was when the writes
14 ; happened.
15
16 ; (die ...) - function to drop a note to stderr and exit with 1.
17 (define (die)
18   (write-line 2 (join (map string (args)) " "))
19   (exit 1))
20
21 ; (read-uint64 FD N) - function to read N consequtive unsigned long from
22 ; file descriptor FD. Return them as a list in order
23 (define (read-uint64 FD N)
24   (let ((B (* N 8)) (BUFFER nil) (HEAD "") (OUT '()))
25     ;; B = number of bytes to read
26     ;; BUFFER = input buffer symbol
27     ;; HEAD = prefix of bytes read but not processed
28     ;; OUT = list of unsigned long to return
29     ;;
30     ;; Note that (read..) might return fewer bytes than asked for so
31     ;; it needs a loop. 
32     (while (and (> B) (> (read FD BUFFER B)))
33       (dec B (length BUFFER)) 
34       (extend HEAD BUFFER)
35       (let ((I (/ (length HEAD) 8)))
36         (when (> I)
37           (extend OUT (unpack (dup "Lu" ) HEAD))
38           (setf HEAD ((* 8 I) HEAD)))))
39     (when (> B) (die "Cannot read" N "unsigned long."))
40     OUT))
41
42 ; (copy-data AT SIZE) - copy the data block of SIZE bytes at position
43 ; AT from OL.fd to FF.fd.
44 (define (copy-data AT SIZE) ; FUSEFILE FF.fd OVERLAY OL.fd
45   (let ((BUFFER nil) (N 0))
46     ;; BUFEER = transfer buffer symbol
47     ;; N = number of bytes written upon each (write...)
48     (when (null? (seek FF.fd AT)) (die "Cannot seek" FUSEFILE AT))
49     (when (null? (seek OL.fd AT)) (die "Cannot seek" OVERLAY AT))
50     (while (> SIZE)
51       (when (<= (read OL.fd BUFFER SIZE))
52         (die "Failed reading" SIZE "bytes"))
53       (dec SIZE (length BUFFER))
54       (while (and (> (length BUFFER))
55                   (setf N (write FF.fd BUFFER (length BUFFER))))
56         (setf BUFFER (N BUFFER)))
57       (when (> (length BUFFER)) (die "Failed writing" AT SIZE ))
58       )))
59
60 ;=== Main program
61
62 ; Require 2 command line arguments, afterinterpreter and script file.
63 (when (!= (length (main-args)) 4)
64   (die "Requires arguments: fusefile overlay"))
65
66 ; Set up globals
67 (setf
68  FUSEFILE (main-args -2)
69  FF.fd (if (open FUSEFILE "u") $it (die "Cannot open" FUSEFILE))
70  FF.end (file-info FUSEFILE 0)
71  OVERLAY (main-args -1)
72  OL.fd (if (open OVERLAY "r") $it (die "Cannot open" OVERLAY))
73  OL.end (file-info OVERLAY 0)
74  OL.N (if (seek OL.fd FF.end)
75           (if (read-uint64 OL.fd 1) ($it 0)
76             (die "Cannot read" OVERLAY "table count."))
77         (die "Cannot seek" OVERLY "to" FF.end))
78  )
79
80 ; Confirm expected file size for overlay
81 (unless (= OL.end (+ FF.end 8 (* OL.N 16)))
82   (die OVERLAY "should be" OL.end "bytes"))
83
84 ; Load the overlay table and copy data according to its entries
85 (dolist (ENTRY (explode
86                 (if (read-uint64 OL.fd (* 2 OL.N)) $it
87                   (die "Cannot read overlay table from" OVERLAY))
88                 2))
89   ;; ENTRY = (begin end) for data block
90   (println (format "copy %s/%d:%d" (cons OVERLAY ENTRY)))
91   (copy-data (ENTRY 0) (- (ENTRY 1) (ENTRY 0))))
92
93 (exit 0)
94 "dumpoverlay.lsp"
95
96 ; E.g., consider a fusefile stack D:A:B:C with overlays A:B:C over D.
97 ; That stack would have been built in a succession of first using
98 ; stack D:A where changes are collated into overlay A, as in the
99 ; following setup:
100 ;
101 ; $ fusefile E -overlay:A D
102 ; ... writes to E (= D:A) goes into A
103 ; $ fusermount -u E
104 ;
105 ; Note that the "D" part might in reality be a more complex
106 ; composition of several file fragments, but in these examples we
107 ; refer to it simply as "D".
108 ;
109 ; Continuing the example, the new stack D:A:B is used for collating
110 ; further changes into B, as in the following setup:
111 ;
112 ; $ fusefile E -overlay:A:B D
113 ; ... writes to E (= D:A:B) actually goes into B
114 ; $ fusermount -u E
115 ;
116 ; Later again, the stack D:A:B:C is used for collating changes into C,
117 ; as in the following setup:
118 ;
119 ; $ fusefile E -overlay:A:B:C D
120 ; ... writes to E (= D:A:B:C) actually goes into C
121 ; ...
122 ; $ fusermount -u D
123 ;
124 ; At that point, one may decide to merge down the overlay C onto a
125 ; D:A:B fusefile and thereby add the C changes over B.
126 ;
127 ; $ fusefile E -overlay:A:B D
128 ; $ merge-overlay E C
129 ; $ rm C
130 ; $ fusermount -u E
131
132 ; Or alternatively, one may decide to merge down the B changes without
133 ; C onto a D:A fusefile and thereby add B to A.
134 ;
135 ; $ fusefile E -overlay:A D
136 ; $ merge-overlay E B
137 ; $ rm B
138 ; $ fusermount -u E
139 ;
140 ; In the latter case the updated overlay A includes the writes
141 ; collated in B and thus now the stack D:A:C would be the same as the
142 ; previous stack D:A:B:C.
143 ;
144 ; End of example.