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