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