xref: /plan9/sys/src/cmd/venti/copy.c (revision 8ffb2bee45f0e1631dbb22b8d323212fb603680f)
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