copy.c (5587B)
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> 8 9 enum 10 { 11 // XXX What to do here? 12 VtMaxLumpSize = 65535, 13 }; 14 15 int changes; 16 int rewrite; 17 int ignoreerrors; 18 int fast; 19 int verbose; 20 int nskip; 21 int nwrite; 22 23 VtConn *zsrc, *zdst; 24 uchar zeroscore[VtScoreSize]; /* all zeros */ 25 26 typedef struct ScoreTree ScoreTree; 27 struct ScoreTree 28 { 29 Avl avl; 30 uchar score[VtScoreSize]; 31 int type; 32 }; 33 34 Avltree *scoretree; 35 Bin *scorebin; 36 37 static int 38 scoretreecmp(Avl *va, Avl *vb) 39 { 40 ScoreTree *a, *b; 41 int i; 42 43 a = (ScoreTree*)va; 44 b = (ScoreTree*)vb; 45 46 i = memcmp(a->score, b->score, VtScoreSize); 47 if(i != 0) 48 return i; 49 return a->type - b->type; 50 } 51 52 static int 53 havevisited(uchar score[VtScoreSize], int type) 54 { 55 ScoreTree a; 56 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 } 63 64 static void 65 markvisited(uchar score[VtScoreSize], int type) 66 { 67 ScoreTree *a; 68 Avl *old; 69 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 } 77 78 void 79 usage(void) 80 { 81 fprint(2, "usage: copy [-fimrVv] [-t type] srchost dsthost score\n"); 82 threadexitsall("usage"); 83 } 84 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; 93 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 } 99 100 if(memcmp(score, vtzeroscore, VtScoreSize) == 0 || memcmp(score, zeroscore, VtScoreSize) == 0) 101 return; 102 103 if(havevisited(score, type)){ 104 nskip++; 105 return; 106 } 107 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; 114 } 115 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; 124 } 125 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; 131 } 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; 137 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; 143 } 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); 156 } 157 break; 158 159 case VtDataType: 160 break; 161 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; 167 } 168 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); 176 } 177 if(!rewrite && memcmp(score, nscore, VtScoreSize) != 0) 178 sysfatal("not rewriting: wrote %V got %V", score, nscore); 179 180 if((type !=0 || base !=0) && verbose){ 181 n = vtzerotruncate(type, buf, n); 182 sha1(buf, n, score, nil); 183 184 for(i = 0; i < depth; i++) 185 fprint(2, " "); 186 fprint(2, "<- %V\n", score); 187 } 188 189 markvisited(score, type); 190 free(buf); 191 } 192 193 void 194 threadmain(int argc, char *argv[]) 195 { 196 int type, n; 197 uchar score[VtScoreSize]; 198 uchar *buf; 199 char *prefix; 200 201 fmtinstall('F', vtfcallfmt); 202 fmtinstall('V', vtscorefmt); 203 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 235 236 if(argc != 3) 237 usage(); 238 239 if(vtparsescore(argv[2], &prefix, score) < 0) 240 sysfatal("could not parse score: %r"); 241 242 buf = vtmallocz(VtMaxLumpSize); 243 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"); 249 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"); 255 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; 265 } 266 if(type == VtMaxType) 267 sysfatal("could not find block %V of any type", score); 268 } 269 270 walk(score, type, VtDirType, 0); 271 if(changes) 272 print("%s:%V (%d pointers rewritten)\n", prefix, score, changes); 273 274 if(verbose) 275 print("%d skipped, %d written\n", nskip, nwrite); 276 277 if(vtsync(zdst) < 0) 278 sysfatal("could not sync dst server: %r"); 279 280 threadexitsall(0); 281 }