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