plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }