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