Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <venti.h>
4 #include <libsec.h>
5 #include <thread.h>
6 #include <avl.h>
7 #include <bin.h>
9 enum
10 {
11 // XXX What to do here?
12 VtMaxLumpSize = 65535,
13 };
15 int changes;
16 int rewrite;
17 int ignoreerrors;
18 int fast;
19 int verbose;
20 int nskip;
21 int nwrite;
23 VtConn *zsrc, *zdst;
24 uchar zeroscore[VtScoreSize]; /* all zeros */
26 typedef struct ScoreTree ScoreTree;
27 struct ScoreTree
28 {
29 Avl avl;
30 uchar score[VtScoreSize];
31 int type;
32 };
34 Avltree *scoretree;
35 Bin *scorebin;
37 static int
38 scoretreecmp(Avl *va, Avl *vb)
39 {
40 ScoreTree *a, *b;
41 int i;
43 a = (ScoreTree*)va;
44 b = (ScoreTree*)vb;
46 i = memcmp(a->score, b->score, VtScoreSize);
47 if(i != 0)
48 return i;
49 return a->type - b->type;
50 }
52 static int
53 havevisited(uchar score[VtScoreSize], int type)
54 {
55 ScoreTree a;
57 if(scoretree == nil)
58 return 0;
59 memmove(a.score, score, VtScoreSize);
60 a.type = type;
61 return lookupavl(scoretree, &a.avl) != nil;
62 }
64 static void
65 markvisited(uchar score[VtScoreSize], int type)
66 {
67 ScoreTree *a;
68 Avl *old;
70 if(scoretree == nil)
71 return;
72 a = binalloc(&scorebin, sizeof *a, 1);
73 memmove(a->score, score, VtScoreSize);
74 a->type = type;
75 insertavl(scoretree, &a->avl, &old);
76 }
78 void
79 usage(void)
80 {
81 fprint(2, "usage: copy [-fimrVv] [-t type] srchost dsthost score\n");
82 threadexitsall("usage");
83 }
85 void
86 walk(uchar score[VtScoreSize], uint type, int base, int depth)
87 {
88 int i, n;
89 uchar *buf;
90 uchar nscore[VtScoreSize];
91 VtEntry e;
92 VtRoot root;
94 if(verbose){
95 for(i = 0; i < depth; i++)
96 fprint(2, " ");
97 fprint(2, "-> %d %d %d %V\n", depth, type, base, score);
98 }
100 if(memcmp(score, vtzeroscore, VtScoreSize) == 0 || memcmp(score, zeroscore, VtScoreSize) == 0)
101 return;
103 if(havevisited(score, type)){
104 nskip++;
105 return;
108 buf = vtmallocz(VtMaxLumpSize);
109 if(fast && vtread(zdst, score, type, buf, VtMaxLumpSize) >= 0){
110 if(verbose)
111 fprint(2, "skip %V\n", score);
112 free(buf);
113 return;
116 n = vtread(zsrc, score, type, buf, VtMaxLumpSize);
117 if(n < 0){
118 if(rewrite){
119 changes++;
120 memmove(score, vtzeroscore, VtScoreSize);
121 }else if(!ignoreerrors)
122 sysfatal("reading block %V (type %d): %r", score, type);
123 return;
126 switch(type){
127 case VtRootType:
128 if(vtrootunpack(&root, buf) < 0){
129 fprint(2, "warning: could not unpack root in %V %d\n", score, type);
130 break;
132 walk(root.prev, VtRootType, 0, depth+1);
133 walk(root.score, VtDirType, 0, depth+1);
134 if(rewrite)
135 vtrootpack(&root, buf); /* walk might have changed score */
136 break;
138 case VtDirType:
139 for(i=0; i*VtEntrySize < n; i++){
140 if(vtentryunpack(&e, buf, i) < 0){
141 fprint(2, "warning: could not unpack entry #%d in %V %d\n", i, score, type);
142 continue;
144 if(!(e.flags & VtEntryActive))
145 continue;
146 walk(e.score, e.type, e.type&VtTypeBaseMask, depth+1);
147 /*
148 * Don't repack unless we're rewriting -- some old
149 * vac files have psize==0 and dsize==0, and these
150 * get rewritten by vtentryunpack to have less strange
151 * block sizes. So vtentryunpack; vtentrypack does not
152 * guarantee to preserve the exact bytes in buf.
153 */
154 if(rewrite)
155 vtentrypack(&e, buf, i);
157 break;
159 case VtDataType:
160 break;
162 default: /* pointers */
163 for(i=0; i<n; i+=VtScoreSize)
164 if(memcmp(buf+i, vtzeroscore, VtScoreSize) != 0)
165 walk(buf+i, type-1, base, depth+1);
166 break;
169 nwrite++;
170 if(vtwrite(zdst, nscore, type, buf, n) < 0){
171 /* figure out score for better error message */
172 /* can't use input argument - might have changed contents */
173 n = vtzerotruncate(type, buf, n);
174 sha1(buf, n, score, nil);
175 sysfatal("writing block %V (type %d): %r", score, type);
177 if(!rewrite && memcmp(score, nscore, VtScoreSize) != 0)
178 sysfatal("not rewriting: wrote %V got %V", score, nscore);
180 if((type !=0 || base !=0) && verbose){
181 n = vtzerotruncate(type, buf, n);
182 sha1(buf, n, score, nil);
184 for(i = 0; i < depth; i++)
185 fprint(2, " ");
186 fprint(2, "<- %V\n", score);
189 markvisited(score, type);
190 free(buf);
193 void
194 threadmain(int argc, char *argv[])
196 int type, n;
197 uchar score[VtScoreSize];
198 uchar *buf;
199 char *prefix;
201 fmtinstall('F', vtfcallfmt);
202 fmtinstall('V', vtscorefmt);
204 type = -1;
205 ARGBEGIN{
206 case 'V':
207 chattyventi++;
208 break;
209 case 'f':
210 fast = 1;
211 break;
212 case 'i':
213 if(rewrite)
214 usage();
215 ignoreerrors = 1;
216 break;
217 case 'm':
218 scoretree = mkavltree(scoretreecmp);
219 break;
220 case 'r':
221 if(ignoreerrors)
222 usage();
223 rewrite = 1;
224 break;
225 case 't':
226 type = atoi(EARGF(usage()));
227 break;
228 case 'v':
229 verbose = 1;
230 break;
231 default:
232 usage();
233 break;
234 }ARGEND
236 if(argc != 3)
237 usage();
239 if(vtparsescore(argv[2], &prefix, score) < 0)
240 sysfatal("could not parse score: %r");
242 buf = vtmallocz(VtMaxLumpSize);
244 zsrc = vtdial(argv[0]);
245 if(zsrc == nil)
246 sysfatal("could not dial src server: %r");
247 if(vtconnect(zsrc) < 0)
248 sysfatal("vtconnect src: %r");
250 zdst = vtdial(argv[1]);
251 if(zdst == nil)
252 sysfatal("could not dial dst server: %r");
253 if(vtconnect(zdst) < 0)
254 sysfatal("vtconnect dst: %r");
256 if(type != -1){
257 n = vtread(zsrc, score, type, buf, VtMaxLumpSize);
258 if(n < 0)
259 sysfatal("could not read block: %r");
260 }else{
261 for(type=0; type<VtMaxType; type++){
262 n = vtread(zsrc, score, type, buf, VtMaxLumpSize);
263 if(n >= 0)
264 break;
266 if(type == VtMaxType)
267 sysfatal("could not find block %V of any type", score);
270 walk(score, type, VtDirType, 0);
271 if(changes)
272 print("%s:%V (%d pointers rewritten)\n", prefix, score, changes);
274 if(verbose)
275 print("%d skipped, %d written\n", nskip, nwrite);
277 if(vtsync(zdst) < 0)
278 sysfatal("could not sync dst server: %r");
280 threadexitsall(0);