Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "all.h"
2
 
3
#define	Nwork	16
4
 
5
int localdirstat(char*, Dir*);
6
int ismatch(char*);
7
void conflict(char*, char*, ...);
8
void error(char*, ...);
9
int isdir(char*);
10
 
11
void worker(int fdf, int fdt, char *from, char *to);
12
vlong	nextoff(void);
13
void	failure(void *, char *note);
14
 
15
QLock	lk;
16
vlong	off;
17
 
18
int errors;
19
int nconf;
20
int donothing;
21
int verbose;
22
char **match;
23
int nmatch;
24
int tempspool = 1;
25
int safeinstall = 1;
26
char *lroot;
27
char *rroot;
28
Db *clientdb;
29
int skip;
30
int douid;
31
char *mkname(char*, int, char*, char*);
32
char localbuf[10240];
33
char remotebuf[10240];
34
int copyfile(char*, char*, char*, Dir*, int, int*);
35
ulong maxnow;
36
int maxn;
37
char *timefile;
38
int timefd;
39
int samecontents(char*, char*);
40
 
41
Db *copyerr;
42
 
43
typedef struct Res Res;
44
struct Res
45
{
46
	char c;
47
	char *name;
48
};
49
 
50
Res *res;
51
int nres;
52
 
53
void 
54
addresolve(int c, char *name)
55
{
56
	if(name[0] == '/')
57
		name++;
58
	res = erealloc(res, (nres+1)*sizeof res[0]);
59
	res[nres].c = c;
60
	res[nres].name = name;
61
	nres++;
62
}
63
 
64
int
65
resolve(char *name)
66
{
67
	int i, len;
68
 
69
	for(i=0; i<nres; i++){
70
		len = strlen(res[i].name);
71
		if(len == 0)
72
			return res[i].c;
73
		if(strncmp(name, res[i].name, len) == 0 && (name[len]=='/' || name[len] == 0))
74
			return res[i].c;
75
	}
76
	return '?';
77
}
78
 
79
void
80
readtimefile(void)
81
{
82
	int n;
83
	char buf[24];
84
 
85
	if((timefd = open(timefile, ORDWR)) < 0
86
	&& (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0)
87
		return;
88
 
89
	n = readn(timefd, buf, sizeof buf);
90
	if(n < sizeof buf)
91
		return;
92
 
93
	maxnow = atoi(buf);
94
	maxn = atoi(buf+12);
95
}
96
 
97
void
98
writetimefile(void)
99
{
100
	char buf[24+1];
101
 
102
	snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn);
103
	pwrite(timefd, buf, 24, 0);
104
}
105
 
106
static void membogus(char**);
107
 
108
void
109
addce(char *local)
110
{
111
	char e[ERRMAX];
112
	Dir d;
113
 
114
	memset(&d, 0, sizeof d);
115
	rerrstr(e, sizeof e);
116
	d.name = atom(e);
117
	d.uid = "";
118
	d.gid = "";
119
	insertdb(copyerr, atom(local), &d);
120
}
121
 
122
void
123
delce(char *local)
124
{
125
	removedb(copyerr, local);
126
}
127
 
128
void
129
chat(char *f, ...)
130
{
131
	Fmt fmt;
132
	char buf[256];
133
	va_list arg;
134
 
135
	if(!verbose)
136
		return;
137
 
138
	fmtfdinit(&fmt, 1, buf, sizeof buf);
139
	va_start(arg, f);
140
	fmtvprint(&fmt, f, arg);
141
	va_end(arg);
142
	fmtfdflush(&fmt);
143
}
144
 
145
void
146
usage(void)
147
{
148
	fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n");
149
	exits("usage");
150
}
151
 
152
int
153
notexists(char *path)
154
{
155
	char buf[ERRMAX];
156
 
157
	if(access(path, AEXIST) >= 0)
158
		return 0;
159
 
160
	rerrstr(buf, sizeof buf);
161
	if(strstr(buf, "entry not found") || strstr(buf, "not exist"))
162
		return 1;
163
 
164
	/* some other error, like network hangup */
165
	return 0;
166
}
167
 
168
int
169
prstopped(int skip, char *name)
170
{
171
	if(!skip) {
172
		fprint(2, "stopped updating log apply time because of %s\n",
173
			name);
174
		skip = 1;
175
	}
176
	return skip;
177
}
178
 
179
void
180
main(int argc, char **argv)
181
{ 
182
	char *f[10], *local, *name, *remote, *s, *t, verb;
183
	int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
184
	int checkedmatch1, checkedmatch2, 
185
		checkedmatch3, checkedmatch4;
186
	ulong now;
187
	Biobuf bin;
188
	Dir dbd, ld, nd, rd;
189
	Avlwalk *w;
190
	Entry *e;
191
 
192
	membogus(argv);
193
	quotefmtinstall();
194
	ARGBEGIN{
195
	case 's':
196
	case 'c':
197
		i = ARGC();
198
		addresolve(i, EARGF(usage()));
199
		break;
200
	case 'n':
201
		donothing = 1;
202
		verbose = 1;
203
		break;
204
	case 'S':
205
		safeinstall = 0;
206
		break;
207
	case 'T':
208
		timefile = EARGF(usage());
209
		break;
210
	case 't':
211
		tempspool = 0;
212
		break;
213
	case 'u':
214
		douid = 1;
215
		break;
216
	case 'v':
217
		verbose++;
218
		break;
219
	default:
220
		usage();
221
	}ARGEND
222
 
223
	if(argc < 3)
224
		usage();
225
 
226
	if(timefile)
227
		readtimefile();
228
 
229
	lroot = argv[1];
230
	if(!isdir(lroot))
231
		sysfatal("bad local root directory");
232
	rroot = argv[2];
233
	if(!isdir(rroot))
234
		sysfatal("bad remote root directory");
235
 
236
	match = argv+3;
237
	nmatch = argc-3;
238
	for(i=0; i<nmatch; i++)
239
		if(match[i][0] == '/')
240
			match[i]++;
241
 
242
	if((clientdb = opendb(argv[0])) == nil)
243
		sysfatal("opendb %q: %r", argv[2]);
244
 
245
	copyerr = opendb(nil);
246
 
247
	skip = 0;
248
	Binit(&bin, 0, OREAD);
249
	for(; s=Brdstr(&bin, '\n', 1); free(s)){
250
		t = estrdup(s);
251
		nf = tokenize(s, f, nelem(f));
252
		if(nf != 10 || strlen(f[2]) != 1){
253
			skip = 1;
254
			fprint(2, "warning: skipping bad log entry <%s>\n", t);
255
			free(t);
256
			continue;
257
		}
258
		free(t);
259
		now = strtoul(f[0], 0, 0);
260
		n = atoi(f[1]);
261
		verb = f[2][0];
262
		name = f[3];
263
		if(now < maxnow || (now==maxnow && n <= maxn))
264
			continue;
265
		local = mkname(localbuf, sizeof localbuf, lroot, name);
266
		if(strcmp(f[4], "-") == 0)
267
			f[4] = f[3];
268
		remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
269
		rd.name = f[4];
270
		rd.mode = strtoul(f[5], 0, 8);
271
		rd.uid = f[6];
272
		rd.gid = f[7];
273
		rd.mtime = strtoul(f[8], 0, 10);
274
		rd.length = strtoll(f[9], 0, 10);
275
		havedb = finddb(clientdb, name, &dbd)>=0;
276
		havelocal = localdirstat(local, &ld)>=0;
277
 
278
		resolve1 = resolve(name);
279
 
280
		/*
281
		 * if(!ismatch(name)){
282
		 *	skip = 1;
283
		 *	continue;
284
		 * }
285
		 * 
286
		 * This check used to be right here, but we want
287
		 * the time to be able to move forward past entries
288
		 * that don't match and have already been applied.
289
		 * So now every path below must checked !ismatch(name)
290
		 * before making any changes to the local file
291
		 * system.  The fake variable checkedmatch
292
		 * tracks whether !ismatch(name) has been checked.
293
		 * If the compiler doesn't produce any used/set
294
		 * warnings, then all the paths should be okay.
295
		 * Even so, we have the asserts to fall back on.
296
		 */
297
		switch(verb){
298
		case 'd':	/* delete file */
299
			delce(local);
300
			if(!havelocal)	/* doesn't exist; who cares? */
301
				break;
302
			if(access(remote, AEXIST) >= 0)	/* got recreated! */
303
				break;
304
			if(!ismatch(name)){
305
				skip = prstopped(skip, name);
306
				continue;
307
			}
308
			SET(checkedmatch1);
309
			if(!havedb){
310
				if(resolve1 == 's')
311
					goto DoRemove;
312
				else if(resolve1 == 'c')
313
					goto DoRemoveDb;
314
				conflict(name, "locally created; will not remove");
315
				skip = 1;
316
				continue;
317
			}
318
			assert(havelocal && havedb);
319
			if(dbd.mtime > rd.mtime)		/* we have a newer file than what was deleted */
320
				break;
321
			if(samecontents(local, remote) > 0){	/* going to get recreated */
322
				chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
323
				break;
324
			}
325
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* locally modified since we downloaded it */
326
				if(resolve1 == 's')
327
					goto DoRemove;
328
				else if(resolve1 == 'c')
329
					break;
330
				conflict(name, "locally modified; will not remove");
331
				skip = 1;
332
				continue;
333
			}
334
		    DoRemove:
335
			USED(checkedmatch1);
336
			assert(ismatch(name));
337
			chat("d %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
338
			if(donothing)
339
				break;
340
			if(remove(local) < 0){
341
				error("removing %q: %r", name);
342
				skip = 1;
343
				continue;
344
			}
345
		    DoRemoveDb:
346
			USED(checkedmatch1);
347
			assert(ismatch(name));
348
			removedb(clientdb, name);
349
			break;
350
 
351
		case 'a':	/* add file */
352
			if(!havedb){
353
				if(!ismatch(name)){
354
					skip = prstopped(skip, name);
355
					continue;
356
				}
357
				SET(checkedmatch2);
358
				if(!havelocal)
359
					goto DoCreate;
360
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
361
					break;
362
				if(samecontents(local, remote) > 0){
363
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
364
					goto DoCreateDb;
365
				}
366
				if(resolve1 == 's')
367
					goto DoCreate;
368
				else if(resolve1 == 'c')
369
					goto DoCreateDb;
370
				conflict(name, "locally created; will not overwrite");
371
				skip = 1;
372
				continue;
373
			}
374
			assert(havedb);
375
			if(dbd.mtime >= rd.mtime)	/* already created this file; ignore */
376
				break;
377
			if(havelocal){
378
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
379
					break;
380
				if(!ismatch(name)){
381
					skip = prstopped(skip, name);
382
					continue;
383
				}
384
				SET(checkedmatch2);
385
				if(samecontents(local, remote) > 0){
386
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
387
					goto DoCreateDb;
388
				}
389
				if(dbd.mtime==ld.mtime && dbd.length==ld.length)
390
					goto DoCreate;
391
				if(resolve1=='s')
392
					goto DoCreate;
393
				else if(resolve1 == 'c')
394
					goto DoCreateDb;
395
				conflict(name, "locally modified; will not overwrite");
396
				skip = 1;
397
				continue;
398
			}
399
			if(!ismatch(name)){
400
				skip = prstopped(skip, name);
401
				continue;
402
			}
403
			SET(checkedmatch2);
404
		    DoCreate:
405
			USED(checkedmatch2);
406
			assert(ismatch(name));
407
			if(notexists(remote)){
408
				addce(local);
409
				/* no skip=1 */
410
				break;;
411
			}
412
			chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
413
			if(donothing)
414
				break;
415
			if(rd.mode&DMDIR){
416
				fd = create(local, OREAD, DMDIR);
417
				if(fd < 0 && isdir(local))
418
					fd = open(local, OREAD);
419
				if(fd  < 0){
420
					error("mkdir %q: %r", name);
421
					skip = 1;
422
					continue;
423
				}
424
				nulldir(&nd);
425
				nd.mode = rd.mode;
426
				if(dirfwstat(fd, &nd) < 0)
427
					fprint(2, "warning: cannot set mode on %q\n", local);
428
				nulldir(&nd);
429
				nd.gid = rd.gid;
430
				if(dirfwstat(fd, &nd) < 0)
431
					fprint(2, "warning: cannot set gid on %q\n", local);
432
				if(douid){
433
					nulldir(&nd);
434
					nd.uid = rd.uid;
435
					if(dirfwstat(fd, &nd) < 0)
436
						fprint(2, "warning: cannot set uid on %q\n", local);
437
				}
438
				close(fd);
439
				rd.mtime = now;
440
			}else{
441
				if(copyfile(local, remote, name, &rd, 1, &k) < 0){
442
					if(k)
443
						addce(local);
444
					skip = 1;
445
					continue;
446
				}
447
			}
448
		    DoCreateDb:
449
			USED(checkedmatch2);
450
			assert(ismatch(name));
451
			insertdb(clientdb, name, &rd);
452
			break;
453
 
454
		case 'c':	/* change contents */
455
			if(!havedb){
456
				if(notexists(remote)){
457
					addce(local);
458
					/* no skip=1 */
459
					break;
460
				}
461
				if(!ismatch(name)){
462
					skip = prstopped(skip, name);
463
					continue;
464
				}
465
				SET(checkedmatch3);
466
				if(resolve1 == 's')
467
					goto DoCopy;
468
				else if(resolve1=='c')
469
					goto DoCopyDb;
470
				if(samecontents(local, remote) > 0){
471
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
472
					goto DoCopyDb;
473
				}
474
				if(havelocal)
475
					conflict(name, "locally created; will not update");
476
				else
477
					conflict(name, "not replicated; will not update");
478
				skip = 1;
479
				continue;
480
			}
481
			if(dbd.mtime >= rd.mtime)		/* already have/had this version; ignore */
482
				break;
483
			if(!ismatch(name)){
484
				skip = prstopped(skip, name);
485
				continue;
486
			}
487
			SET(checkedmatch3);
488
			if(!havelocal){
489
				if(notexists(remote)){
490
					addce(local);
491
					/* no skip=1 */
492
					break;
493
				}
494
				if(resolve1 == 's')
495
					goto DoCopy;
496
				else if(resolve1 == 'c')
497
					break;
498
				conflict(name, "locally removed; will not update");
499
				skip = 1;
500
				continue;
501
			}
502
			assert(havedb && havelocal);
503
			if(dbd.mtime != ld.mtime || dbd.length != ld.length){
504
				if(notexists(remote)){
505
					addce(local);
506
					/* no skip=1 */
507
					break;
508
				}
509
				if(samecontents(local, remote) > 0){
510
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
511
					goto DoCopyDb;
512
				}
513
				if(resolve1 == 's')
514
					goto DoCopy;
515
				else if(resolve1 == 'c')
516
					goto DoCopyDb;
517
				conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
518
				skip = 1;
519
				continue;
520
			}
521
		    DoCopy:
522
			USED(checkedmatch3);
523
			assert(ismatch(name));
524
			if(notexists(remote)){
525
				addce(local);
526
				/* no skip=1 */
527
				break;
528
			}
529
			chat("c %q\n", name);
530
			if(donothing)
531
				break;
532
			if(copyfile(local, remote, name, &rd, 0, &k) < 0){
533
				if(k)
534
					addce(local);
535
				skip = 1;
536
				continue;
537
			}
538
		    DoCopyDb:
539
			USED(checkedmatch3);
540
			assert(ismatch(name));
541
			if(!havedb){
542
				if(havelocal)
543
					dbd = ld;
544
				else
545
					dbd = rd;
546
			}
547
			dbd.mtime = rd.mtime;
548
			dbd.length = rd.length;
549
			insertdb(clientdb, name, &dbd);
550
			break;			
551
 
552
		case 'm':	/* change metadata */
553
			if(!havedb){
554
				if(notexists(remote)){
555
					addce(local);
556
					/* no skip=1 */
557
					break;
558
				}
559
				if(!ismatch(name)){
560
					skip = prstopped(skip, name);
561
					continue;
562
				}
563
				SET(checkedmatch4);
564
				if(resolve1 == 's'){
565
					USED(checkedmatch4);
566
					SET(checkedmatch2);
567
					goto DoCreate;
568
				}
569
				else if(resolve1 == 'c')
570
					goto DoMetaDb;
571
				if(havelocal)
572
					conflict(name, "locally created; will not update metadata");
573
				else
574
					conflict(name, "not replicated; will not update metadata");
575
				skip = 1;
576
				continue;
577
			}
578
			if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime)		/* have newer version; ignore */
579
				break;
580
			if((dbd.mode&DMDIR) && dbd.mtime > now)
581
				break;
582
			if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
583
				break;
584
			if(!havelocal){
585
				if(notexists(remote)){
586
					addce(local);
587
					/* no skip=1 */
588
					break;
589
				}
590
				if(!ismatch(name)){
591
					skip = prstopped(skip, name);
592
					continue;
593
				}
594
				SET(checkedmatch4);
595
				if(resolve1 == 's'){
596
					USED(checkedmatch4);
597
					SET(checkedmatch2);
598
					goto DoCreate;
599
				}
600
				else if(resolve1 == 'c')
601
					break;
602
				conflict(name, "locally removed; will not update metadata");
603
				skip = 1;
604
				continue;
605
			}
606
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* this check might be overkill */
607
				if(notexists(remote)){
608
					addce(local);
609
					/* no skip=1 */
610
					break;
611
				}
612
				if(!ismatch(name)){
613
					skip = prstopped(skip, name);
614
					continue;
615
				}
616
				SET(checkedmatch4);
617
				if(resolve1 == 's' || samecontents(local, remote) > 0)
618
					goto DoMeta;
619
				else if(resolve1 == 'c')
620
					break;
621
				conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
622
					dbd.mtime != ld.mtime ? "mtime" :
623
					dbd.length != ld.length ? "length" : 
624
					"unknown",
625
					rd.uid, rd.gid, rd.mode);
626
				skip = 1;
627
				continue;
628
			}
629
			if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
630
				if(notexists(remote)){
631
					addce(local);
632
					/* no skip=1 */
633
					break;
634
				}
635
				if(!ismatch(name)){
636
					skip = prstopped(skip, name);
637
					continue;
638
				}
639
				SET(checkedmatch4);
640
				if(resolve1 == 's')
641
					goto DoMeta;
642
				else if(resolve1 == 'c')
643
					break;
644
				conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
645
				skip = 1;
646
				continue;
647
			}
648
			if(!ismatch(name)){
649
				skip = prstopped(skip, name);
650
				continue;
651
			}
652
			SET(checkedmatch4);
653
		    DoMeta:
654
			USED(checkedmatch4);
655
			assert(ismatch(name));
656
			if(notexists(remote)){
657
				addce(local);
658
				/* no skip=1 */
659
				break;
660
			}
661
			chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
662
			if(donothing)
663
				break;
664
			nulldir(&nd);
665
			nd.gid = rd.gid;
666
			nd.mode = rd.mode;
667
			if(douid)
668
				nd.uid = rd.uid;
669
			if(dirwstat(local, &nd) < 0){
670
				error("dirwstat %q: %r", name);
671
				skip = 1;
672
				continue;
673
			}
674
		    DoMetaDb:
675
			USED(checkedmatch4);
676
			assert(ismatch(name));
677
			if(!havedb){
678
				if(havelocal)
679
					dbd = ld;
680
				else
681
					dbd = rd;
682
			}
683
			if(dbd.mode&DMDIR)
684
				dbd.mtime = now;
685
			dbd.gid = rd.gid;
686
			dbd.mode = rd.mode;
687
			if(douid)
688
				dbd.uid = rd.uid;
689
			insertdb(clientdb, name, &dbd);
690
			break;
691
		}
692
		if(!skip && !donothing){
693
			maxnow = now;
694
			maxn = n;
695
		}
696
	}
697
 
698
	w = avlwalk(copyerr->avl);
699
	while(e = (Entry*)avlnext(w))
700
		error("copying %q: %s\n", e->name, e->d.name);
701
 
702
	if(timefile)
703
		writetimefile();
704
	if(nconf)
705
		exits("conflicts");
706
 
707
	if(errors)
708
		exits("errors");
709
	exits(nil);
710
}
711
 
712
 
713
char*
714
mkname(char *buf, int nbuf, char *a, char *b)
715
{
716
	if(strlen(a)+strlen(b)+2 > nbuf)
717
		sysfatal("name too long");
718
 
719
	strcpy(buf, a);
720
	if(a[strlen(a)-1] != '/')
721
		strcat(buf, "/");
722
	strcat(buf, b);
723
	return buf;
724
}
725
 
726
int
727
isdir(char *s)
728
{
729
	ulong m;
730
	Dir *d;
731
 
732
	if((d = dirstat(s)) == nil)
733
		return 0;
734
	m = d->mode;
735
	free(d);
736
	return (m&DMDIR) != 0;
737
}
738
 
739
void
740
conflict(char *name, char *f, ...)
741
{
742
	char *s;
743
	va_list arg;
744
 
745
	va_start(arg, f);
746
	s = vsmprint(f, arg);
747
	va_end(arg);
748
 
749
	fprint(2, "! %s: %s\n", name, s);
750
	free(s);
751
 
752
	nconf++;
753
}
754
 
755
void
756
error(char *f, ...)
757
{
758
	char *s;
759
	va_list arg;
760
 
761
	va_start(arg, f);
762
	s = vsmprint(f, arg);
763
	va_end(arg);
764
	fprint(2, "error: %s\n", s);
765
	free(s);
766
	errors = 1;
767
}
768
 
769
int
770
ismatch(char *s)
771
{
772
	int i, len;
773
 
774
	if(nmatch == 0)
775
		return 1;
776
	for(i=0; i<nmatch; i++){
777
		len = strlen(match[i]);
778
		if(len == 0)
779
			return 1;
780
		if(strncmp(s, match[i], len) == 0 && (s[len]=='/' || s[len] == 0))
781
			return 1;
782
	}
783
	return 0;
784
}
785
 
786
int
787
localdirstat(char *name, Dir *d)
788
{
789
	static Dir *d2;
790
 
791
	free(d2);
792
	if((d2 = dirstat(name)) == nil)
793
		return -1;
794
	*d = *d2;
795
	return 0;
796
}
797
 
798
enum { DEFB = 8192 };
799
 
800
static int
801
cmp1(int fd1, int fd2)
802
{
803
	char buf1[DEFB];
804
	char buf2[DEFB];
805
	int n1, n2;
806
 
807
	for(;;){
808
		n1 = readn(fd1, buf1, DEFB);
809
		n2 = readn(fd2, buf2, DEFB);
810
		if(n1 < 0 || n2 < 0)
811
			return -1;
812
		if(n1 != n2)
813
			return 0;
814
		if(n1 == 0)
815
			return 1;
816
		if(memcmp(buf1, buf2, n1) != 0)
817
			return 0;
818
	}
819
}
820
 
821
static int
822
copy1(int fdf, int fdt, char *from, char *to)
823
{
824
	int i, n, rv, pid[Nwork];
825
	Waitmsg *w;
826
 
827
	n = 0;
828
	off = 0;
829
	for(i=0; i<Nwork; i++){
830
		switch(pid[n] = rfork(RFPROC|RFMEM)){
831
		case 0:
832
			notify(failure);
833
			worker(fdf, fdt, from, to);
834
		case -1:
835
			break;
836
		default:
837
			n++;
838
			break;
839
		}
840
	}
841
	if(n == 0){
842
		fprint(2, "cp: rfork: %r\n");
843
		return -1;
844
	}
845
 
846
	rv = 0;
847
	while((w = wait()) != nil){
848
		if(w->msg[0]){
849
			rv = -1;
850
			for(i=0; i<n; i++)
851
				if(pid[i] > 0)
852
					postnote(PNPROC, pid[i], "failure");
853
		}
854
		free(w);
855
	}
856
	return rv;
857
}
858
 
859
void
860
worker(int fdf, int fdt, char *from, char *to)
861
{
862
	char buf[DEFB], *bp;
863
	long len, n;
864
	vlong o;
865
 
866
	len = sizeof(buf);
867
	bp = buf;
868
	o = nextoff();
869
 
870
	while(n = pread(fdf, bp, len, o)){
871
		if(n < 0){
872
			fprint(2, "reading %s: %r\n", from);
873
			_exits("bad");
874
		}
875
		if(pwrite(fdt, buf, n, o) != n){
876
			fprint(2, "writing %s: %r\n", to);
877
			_exits("bad");
878
		}
879
		bp += n;
880
		o += n;
881
		len -= n;
882
		if(len == 0){
883
			len = sizeof buf;
884
			bp = buf;
885
			o = nextoff();
886
		}
887
	}
888
	_exits(nil);
889
}
890
 
891
vlong
892
nextoff(void)
893
{
894
	vlong o;
895
 
896
	qlock(&lk);
897
	o = off;
898
	off += DEFB;
899
	qunlock(&lk);
900
 
901
	return o;
902
}
903
 
904
void
905
failure(void*, char *note)
906
{
907
	if(strcmp(note, "failure") == 0)
908
		_exits(nil);
909
	noted(NDFLT);
910
}
911
 
912
 
913
static int
914
opentemp(char *template)
915
{
916
	int fd, i;
917
	char *p;
918
 
919
	p = estrdup(template);
920
	fd = -1;
921
	for(i=0; i<10; i++){
922
		mktemp(p);
923
		if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0)
924
			break;
925
		strcpy(p, template);
926
	}
927
	if(fd < 0)
928
		return -1;
929
	strcpy(template, p);
930
	free(p);
931
	return fd;
932
}
933
 
934
static int
935
copytotemp(char *remote, int rfd, Dir *d0)
936
{
937
	int tfd;
938
	char tmp[32];
939
	Dir *d1;
940
 
941
	strcpy(tmp, "/tmp/replicaXXXXXXXX");
942
	tfd = opentemp(tmp);
943
	if(tfd < 0)
944
		return -1;
945
	if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
946
		close(tfd);
947
		return -1;
948
	}
949
 
950
	if(d0->qid.path != d1->qid.path
951
	|| d0->qid.vers != d1->qid.vers
952
	|| d0->mtime != d1->mtime
953
	|| d0->length != d1->length){
954
		/* file changed underfoot; go around again */
955
		free(d1);
956
		close(tfd);
957
		return -2;
958
	}
959
	free(d1);
960
	return tfd;
961
}
962
 
963
int
964
copyfile(char *local, char *remote, char *name, Dir *d, int dowstat,
965
	int *printerror)
966
{
967
	Dir *d0, *dl;
968
	Dir nd;
969
	int rfd, tfd, wfd, didcreate;
970
	char tmp[32], *p, *safe;
971
	char err[ERRMAX];
972
 
973
	do {
974
		*printerror = 0;
975
		if((rfd = open(remote, OREAD)) < 0)
976
			return -1;
977
 
978
		d0 = dirfstat(rfd);
979
		if(d0 == nil){
980
			close(rfd);
981
			return -1;
982
		}
983
		*printerror = 1;
984
		if(!tempspool){
985
			tfd = rfd;
986
			goto DoCopy;
987
		}
988
 
989
		tfd = copytotemp(remote, rfd, d0);
990
		close(rfd);
991
		if (tfd < 0) {
992
			free(d0);
993
			if (tfd == -1)
994
				return -1;
995
		}
996
	} while(tfd == -2);
997
	if(seek(tfd, 0, 0) != 0){
998
		close(tfd);
999
		free(d0);
1000
		return -1;
1001
	}
1002
 
1003
DoCopy:
1004
	/*
1005
	 * clumsy but important hack to do safeinstall-like installs.
1006
	 */
1007
	p = strchr(name, '/');
1008
	if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){
1009
		/* 
1010
		 * remove bin/_targ
1011
		 */
1012
		safe = emalloc(strlen(local)+2);
1013
		strcpy(safe, local);
1014
		p = strrchr(safe, '/')+1;
1015
		memmove(p+1, p, strlen(p)+1);
1016
		p[0] = '_';
1017
		remove(safe);	/* ignore failure */
1018
 
1019
		/*
1020
		 * rename bin/targ to bin/_targ
1021
		 */
1022
		nulldir(&nd);
1023
		nd.name = p;
1024
		if(dirwstat(local, &nd) < 0)
1025
			fprint(2, "warning: rename %s to %s: %r\n", local, p);
1026
	}
1027
 
1028
	didcreate = 0;
1029
	if((dl = dirstat(local)) == nil){
1030
		if((wfd = create(local, OWRITE, 0)) >= 0){
1031
			didcreate = 1;
1032
			goto okay;
1033
		}
1034
		goto err;
1035
	}else{
1036
		if((wfd = open(local, OTRUNC|OWRITE)) >= 0)
1037
			goto okay;
1038
		rerrstr(err, sizeof err);
1039
		if(strstr(err, "permission") == nil)
1040
			goto err;
1041
		nulldir(&nd);
1042
		/*
1043
		 * Assume the person running pull is in the appropriate
1044
		 * groups.  We could set 0666 instead, but I'm worried
1045
		 * about leaving the file world-readable or world-writable
1046
		 * when it shouldn't be.
1047
		 */
1048
		nd.mode = dl->mode | 0660;
1049
		if(nd.mode == dl->mode)
1050
			goto err;
1051
		if(dirwstat(local, &nd) < 0)
1052
			goto err;
1053
		if((wfd = open(local, OTRUNC|OWRITE)) >= 0){
1054
			nd.mode = dl->mode;
1055
			if(dirfwstat(wfd, &nd) < 0)
1056
				fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode);
1057
			goto okay;
1058
		}
1059
		nd.mode = dl->mode;
1060
		if(dirwstat(local, &nd) < 0)
1061
			fprint(2, "warning: set mode on %s to %luo to open; open failed; cannot set mode back to %luo: %r\n", local, nd.mode|0660, nd.mode);
1062
		goto err;
1063
	}
1064
 
1065
err:
1066
	close(tfd);
1067
	free(d0);
1068
	free(dl);
1069
	return -1;
1070
 
1071
okay:
1072
	free(dl);
1073
	if(copy1(tfd, wfd, tmp, local) < 0){
1074
		close(tfd);
1075
		close(wfd);
1076
		free(d0);
1077
		return -1;
1078
	}
1079
	close(tfd);
1080
	if(didcreate || dowstat){
1081
		nulldir(&nd);
1082
		nd.mode = d->mode;
1083
		if(dirfwstat(wfd, &nd) < 0)
1084
			fprint(2, "warning: cannot set mode on %s\n", local);
1085
		nulldir(&nd);
1086
		nd.gid = d->gid;
1087
		if(dirfwstat(wfd, &nd) < 0)
1088
			fprint(2, "warning: cannot set gid on %s\n", local);
1089
		if(douid){
1090
			nulldir(&nd);
1091
			nd.uid = d->uid;
1092
			if(dirfwstat(wfd, &nd) < 0)
1093
				fprint(2, "warning: cannot set uid on %s\n", local);
1094
		}
1095
	}
1096
	d->mtime = d0->mtime;
1097
	d->length = d0->length;
1098
	nulldir(&nd);
1099
	nd.mtime = d->mtime;
1100
	if(dirfwstat(wfd, &nd) < 0)
1101
		fprint(2, "warning: cannot set mtime on %s\n", local);
1102
	free(d0);
1103
 
1104
	close(wfd);
1105
	return 0;
1106
}
1107
 
1108
int
1109
samecontents(char *local, char *remote)
1110
{
1111
	Dir *d0, *d1;
1112
	int rfd, tfd, lfd, ret;
1113
 
1114
	/* quick check: sizes must match */
1115
	d1 = nil;
1116
	if((d0 = dirstat(local)) == nil || (d1 = dirstat(remote)) == nil){
1117
		free(d0);
1118
		free(d1);
1119
		return -1;
1120
	}
1121
	if(d0->length != d1->length){
1122
		free(d0);
1123
		free(d1);
1124
		return 0;
1125
	}
1126
 
1127
	do {
1128
		if((rfd = open(remote, OREAD)) < 0)
1129
			return -1;
1130
		d0 = dirfstat(rfd);
1131
		if(d0 == nil){
1132
			close(rfd);
1133
			return -1;
1134
		}
1135
 
1136
		tfd = copytotemp(remote, rfd, d0);
1137
		close(rfd);
1138
		free(d0);
1139
		if (tfd == -1)
1140
			return -1;
1141
	} while(tfd == -2);
1142
	if(seek(tfd, 0, 0) != 0){
1143
		close(tfd);
1144
		return -1;
1145
	}
1146
 
1147
	/*
1148
	 * now compare
1149
	 */
1150
	if((lfd = open(local, OREAD)) < 0){
1151
		close(tfd);
1152
		return -1;
1153
	}
1154
 
1155
	ret = cmp1(lfd, tfd);
1156
	close(lfd);
1157
	close(tfd);
1158
	return ret;
1159
}
1160
 
1161
/*
1162
 * Applylog might try to overwrite itself.
1163
 * To avoid problems with this, we copy ourselves
1164
 * into /tmp and then re-exec.
1165
 */
1166
char *rmargv0;
1167
 
1168
static void
1169
rmself(void)
1170
{
1171
	remove(rmargv0);
1172
}
1173
 
1174
static int
1175
genopentemp(char *template, int mode, int perm)
1176
{
1177
	int fd, i;
1178
	char *p;	
1179
 
1180
	p = estrdup(template);
1181
	fd = -1;
1182
	for(i=0; i<10; i++){
1183
		mktemp(p);
1184
		if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
1185
			break;
1186
		strcpy(p, template);
1187
	}
1188
	if(fd < 0)
1189
		sysfatal("could not create temporary file");
1190
 
1191
	strcpy(template, p);
1192
	free(p);
1193
 
1194
	return fd;
1195
}
1196
 
1197
static void
1198
membogus(char **argv)
1199
{
1200
	int n, fd, wfd;
1201
	char template[50], buf[1024];
1202
 
1203
	if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) {
1204
		rmargv0 = argv[0];
1205
		atexit(rmself);
1206
		return;
1207
	}
1208
 
1209
	if((fd = open(argv[0], OREAD)) < 0)
1210
		return;
1211
 
1212
	strcpy(template, "/tmp/_applylog_XXXXXX");
1213
	if((wfd = genopentemp(template, OWRITE, 0700)) < 0)
1214
		return;
1215
 
1216
	while((n = read(fd, buf, sizeof buf)) > 0)
1217
		if(write(wfd, buf, n) != n)
1218
			goto Error;
1219
 
1220
	if(n != 0)
1221
		goto Error;
1222
 
1223
	close(fd);
1224
	close(wfd);
1225
 
1226
	argv[0] = template;
1227
	exec(template, argv);
1228
	fprint(2, "exec error %r\n");
1229
 
1230
Error:
1231
	close(fd);
1232
	close(wfd);
1233
	remove(template);
1234
	return;
1235
}