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 <u.h>
2
#include <libc.h>
3
#include <auth.h>
4
#include <bio.h>
5
 
6
enum{
7
	LEN	= 8*1024,
8
	HUNKS	= 128,
9
 
10
	/*
11
	 * types of destination file sytems
12
	 */
13
	Kfs = 0,
14
	Fs,
15
	Archive,
16
};
17
 
18
typedef struct File	File;
19
 
20
struct File{
21
	char	*new;
22
	char	*elem;
23
	char	*old;
24
	char	*uid;
25
	char	*gid;
26
	ulong	mode;
27
};
28
 
29
void	arch(Dir*);
30
void	copy(Dir*);
31
int	copyfile(File*, Dir*, int);
32
void*	emalloc(ulong);
33
void	error(char *, ...);
34
void	freefile(File*);
35
File*	getfile(File*);
36
char*	getmode(char*, ulong*);
37
char*	getname(char*, char**);
38
char*	getpath(char*);
39
void	kfscmd(char *);
40
void	mkdir(Dir*);
41
int	mkfile(File*);
42
void	mkfs(File*, int);
43
char*	mkpath(char*, char*);
44
void	mktree(File*, int);
45
void	mountkfs(char*);
46
void	printfile(File*);
47
void	setnames(File*);
48
void	setusers(void);
49
void	skipdir(void);
50
char*	strdup(char*);
51
int	uptodate(Dir*, char*);
52
void	usage(void);
53
void	warn(char *, ...);
54
 
55
Biobuf	*b;
56
Biobufhdr bout;			/* stdout when writing archive */
57
uchar	boutbuf[2*LEN];
58
char	newfile[LEN];
59
char	oldfile[LEN];
60
char	*proto;
61
char	*cputype;
62
char	*users;
63
char	*oldroot;
64
char	*newroot;
65
char	*prog = "mkfs";
66
int	lineno;
67
char	*buf;
68
char	*zbuf;
69
int	buflen = 1024-8;
70
int	indent;
71
int	verb;
72
int	modes;
73
int	ream;
74
int	debug;
75
int	xflag;
76
int	sfd;
77
int	fskind;			/* Kfs, Fs, Archive */
78
int	setuid;			/* on Fs: set uid and gid? */
79
char	*user;
80
 
81
void
82
main(int argc, char **argv)
83
{
84
	File file;
85
	char *name;
86
	int i, errs;
87
 
88
	quotefmtinstall();
89
	user = getuser();
90
	name = "";
91
	memset(&file, 0, sizeof file);
92
	file.new = "";
93
	file.old = 0;
94
	oldroot = "";
95
	newroot = "/n/kfs";
96
	users = 0;
97
	fskind = Kfs;
98
	ARGBEGIN{
99
	case 'a':
100
		if(fskind != Kfs) {
101
			fprint(2, "cannot use -a with -d\n");
102
			usage();
103
		}
104
		fskind = Archive;
105
		newroot = "";
106
		Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
107
		break;
108
	case 'd':
109
		if(fskind != Kfs) {
110
			fprint(2, "cannot use -d with -a\n");
111
			usage();
112
		}
113
		fskind = Fs;
114
		newroot = ARGF();
115
		break;
116
	case 'D':
117
		debug = 1;
118
		break;
119
	case 'n':
120
		name = EARGF(usage());
121
		break;
122
	case 'p':
123
		modes = 1;
124
		break;
125
	case 'r':
126
		ream = 1;
127
		break;
128
	case 's':
129
		oldroot = ARGF();
130
		break;
131
	case 'u':
132
		users = ARGF();
133
		break;
134
	case 'U':
135
		setuid = 1;
136
		break;
137
	case 'v':
138
		verb = 1;
139
		break;
140
	case 'x':
141
		xflag = 1;
142
		break;
143
	case 'z':
144
		buflen = atoi(ARGF())-8;
145
		break;
146
	default:
147
		usage();
148
	}ARGEND
149
 
150
	if(!argc)	
151
		usage();
152
 
153
	buf = emalloc(buflen);
154
	zbuf = emalloc(buflen);
155
	memset(zbuf, 0, buflen);
156
 
157
	mountkfs(name);
158
	kfscmd("allow");
159
	proto = "users";
160
	setusers();
161
	cputype = getenv("cputype");
162
	if(cputype == 0)
163
		cputype = "68020";
164
 
165
	errs = 0;
166
	for(i = 0; i < argc; i++){
167
		proto = argv[i];
168
		fprint(2, "processing %q\n", proto);
169
 
170
		b = Bopen(proto, OREAD);
171
		if(!b){
172
			fprint(2, "%q: can't open %q: skipping\n", prog, proto);
173
			errs++;
174
			continue;
175
		}
176
 
177
		lineno = 0;
178
		indent = 0;
179
		mkfs(&file, -1);
180
		Bterm(b);
181
	}
182
	fprint(2, "file system made\n");
183
	kfscmd("disallow");
184
	kfscmd("sync");
185
	if(errs)
186
		exits("skipped protos");
187
	if(fskind == Archive){
188
		Bprint(&bout, "end of archive\n");
189
		Bterm(&bout);
190
	}
191
	exits(0);
192
}
193
 
194
void
195
mkfs(File *me, int level)
196
{
197
	File *child;
198
	int rec;
199
 
200
	child = getfile(me);
201
	if(!child)
202
		return;
203
	if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
204
		rec = child->elem[0] == '+';
205
		free(child->new);
206
		child->new = strdup(me->new);
207
		setnames(child);
208
		mktree(child, rec);
209
		freefile(child);
210
		child = getfile(me);
211
	}
212
	while(child && indent > level){
213
		if(mkfile(child))
214
			mkfs(child, indent);
215
		freefile(child);
216
		child = getfile(me);
217
	}
218
	if(child){
219
		freefile(child);
220
		Bseek(b, -Blinelen(b), 1);
221
		lineno--;
222
	}
223
}
224
 
225
void
226
mktree(File *me, int rec)
227
{
228
	File child;
229
	Dir *d;
230
	int i, n, fd;
231
 
232
	fd = open(oldfile, OREAD);
233
	if(fd < 0){
234
		warn("can't open %q: %r", oldfile);
235
		return;
236
	}
237
 
238
	child = *me;
239
	while((n = dirread(fd, &d)) > 0){
240
		for(i = 0; i < n; i++){
241
			child.new = mkpath(me->new, d[i].name);
242
			if(me->old)
243
				child.old = mkpath(me->old, d[i].name);
244
			child.elem = d[i].name;
245
			setnames(&child);
246
			if(copyfile(&child, &d[i], 1) && rec)
247
				mktree(&child, rec);
248
			free(child.new);
249
			if(child.old)
250
				free(child.old);
251
		}
252
	}
253
	close(fd);
254
}
255
 
256
int
257
mkfile(File *f)
258
{
259
	Dir *dir;
260
 
261
	if((dir = dirstat(oldfile)) == nil){
262
		warn("can't stat file %q: %r", oldfile);
263
		skipdir();
264
		return 0;
265
	}
266
	return copyfile(f, dir, 0);
267
}
268
 
269
int
270
copyfile(File *f, Dir *d, int permonly)
271
{
272
	ulong mode;
273
	Dir nd;
274
 
275
	if(xflag){
276
		Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length);
277
		return (d->mode & DMDIR) != 0;
278
	}
279
	if(verb && (fskind == Archive || ream))
280
		fprint(2, "%q\n", f->new);
281
	d->name = f->elem;
282
	if(d->type != 'M'){
283
		d->uid = "sys";
284
		d->gid = "sys";
285
		mode = (d->mode >> 6) & 7;
286
		d->mode |= mode | (mode << 3);
287
	}
288
	if(strcmp(f->uid, "-") != 0)
289
		d->uid = f->uid;
290
	if(strcmp(f->gid, "-") != 0)
291
		d->gid = f->gid;
292
	if(fskind == Fs && !setuid){
293
		d->uid = "";
294
		d->gid = "";
295
	}
296
	if(f->mode != ~0){
297
		if(permonly)
298
			d->mode = (d->mode & ~0666) | (f->mode & 0666);
299
		else if((d->mode&DMDIR) != (f->mode&DMDIR))
300
			warn("inconsistent mode for %q", f->new);
301
		else
302
			d->mode = f->mode;
303
	}
304
	if(!uptodate(d, newfile)){
305
		if(verb && (fskind != Archive && ream == 0))
306
			fprint(2, "%q\n", f->new);
307
		if(d->mode & DMDIR)
308
			mkdir(d);
309
		else
310
			copy(d);
311
	}else if(modes){
312
		nulldir(&nd);
313
		nd.mode = d->mode;
314
		nd.gid = d->gid;
315
		nd.mtime = d->mtime;
316
		if(verb && (fskind != Archive && ream == 0))
317
			fprint(2, "%q\n", f->new);
318
		if(dirwstat(newfile, &nd) < 0)
319
			warn("can't set modes for %q: %r", f->new);
320
		nulldir(&nd);
321
		nd.uid = d->uid;
322
		dirwstat(newfile, &nd);
323
	}
324
	return (d->mode & DMDIR) != 0;
325
}
326
 
327
/*
328
 * check if file to is up to date with
329
 * respect to the file represented by df
330
 */
331
int
332
uptodate(Dir *df, char *to)
333
{
334
	int ret;
335
	Dir *dt;
336
 
337
	if(fskind == Archive || ream || (dt = dirstat(to)) == nil)
338
		return 0;
339
	ret = dt->mtime >= df->mtime;
340
	free(dt);
341
	return ret;
342
}
343
 
344
void
345
copy(Dir *d)
346
{
347
	char cptmp[LEN], *p;
348
	int f, t, n, needwrite, nowarnyet = 1;
349
	vlong tot, len;
350
	Dir nd;
351
 
352
	f = open(oldfile, OREAD);
353
	if(f < 0){
354
		warn("can't open %q: %r", oldfile);
355
		return;
356
	}
357
	t = -1;
358
	if(fskind == Archive)
359
		arch(d);
360
	else{
361
		strcpy(cptmp, newfile);
362
		p = utfrrune(cptmp, L'/');
363
		if(!p)
364
			error("internal temporary file error");
365
		strcpy(p+1, "__mkfstmp");
366
		t = create(cptmp, OWRITE, 0666);
367
		if(t < 0){
368
			warn("can't create %q: %r", newfile);
369
			close(f);
370
			return;
371
		}
372
	}
373
 
374
	needwrite = 0;
375
	for(tot = 0; tot < d->length; tot += n){
376
		len = d->length - tot;
377
		/* don't read beyond d->length */
378
		if (len > buflen)
379
			len = buflen;
380
		n = read(f, buf, len);
381
		if(n <= 0) {
382
			if(n < 0 && nowarnyet) {
383
				warn("can't read %q: %r", oldfile);
384
				nowarnyet = 0;
385
			}
386
			/*
387
			 * don't quit: pad to d->length (in pieces) to agree
388
			 * with the length in the header, already emitted.
389
			 */
390
			memset(buf, 0, len);
391
			n = len;
392
		}
393
		if(fskind == Archive){
394
			if(Bwrite(&bout, buf, n) != n)
395
				error("write error: %r");
396
		}else if(memcmp(buf, zbuf, n) == 0){
397
			if(seek(t, n, 1) < 0)
398
				error("can't write zeros to %q: %r", newfile);
399
			needwrite = 1;
400
		}else{
401
			if(write(t, buf, n) < n)
402
				error("can't write %q: %r", newfile);
403
			needwrite = 0;
404
		}
405
	}
406
	close(f);
407
	if(needwrite){
408
		if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)
409
			error("can't write zero at end of %q: %r", newfile);
410
	}
411
	if(tot != d->length){
412
		/* this should no longer happen */
413
		warn("wrong number of bytes written to %q (was %lld should be %lld)\n",
414
			newfile, tot, d->length);
415
		if(fskind == Archive){
416
			warn("seeking to proper position\n");
417
			/* does no good if stdout is a pipe */
418
			Bseek(&bout, d->length - tot, 1);
419
		}
420
	}
421
	if(fskind == Archive)
422
		return;
423
	remove(newfile);
424
	nulldir(&nd);
425
	nd.mode = d->mode;
426
	nd.gid = d->gid;
427
	nd.mtime = d->mtime;
428
	nd.name = d->name;
429
	if(dirfwstat(t, &nd) < 0)
430
		error("can't move tmp file to %q: %r", newfile);
431
	nulldir(&nd);
432
	nd.uid = d->uid;
433
	dirfwstat(t, &nd);
434
	close(t);
435
}
436
 
437
void
438
mkdir(Dir *d)
439
{
440
	Dir *d1;
441
	Dir nd;
442
	int fd;
443
 
444
	if(fskind == Archive){
445
		arch(d);
446
		return;
447
	}
448
	fd = create(newfile, OREAD, d->mode);
449
	nulldir(&nd);
450
	nd.mode = d->mode;
451
	nd.gid = d->gid;
452
	nd.mtime = d->mtime;
453
	if(fd < 0){
454
		if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){
455
			free(d1);
456
			error("can't create %q", newfile);
457
		}
458
		free(d1);
459
		if(dirwstat(newfile, &nd) < 0)
460
			warn("can't set modes for %q: %r", newfile);
461
		nulldir(&nd);
462
		nd.uid = d->uid;
463
		dirwstat(newfile, &nd);
464
		return;
465
	}
466
	if(dirfwstat(fd, &nd) < 0)
467
		warn("can't set modes for %q: %r", newfile);
468
	nulldir(&nd);
469
	nd.uid = d->uid;
470
	dirfwstat(fd, &nd);
471
	close(fd);
472
}
473
 
474
void
475
arch(Dir *d)
476
{
477
	Bprint(&bout, "%q %luo %q %q %lud %lld\n",
478
		newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
479
}
480
 
481
char *
482
mkpath(char *prefix, char *elem)
483
{
484
	char *p;
485
	int n;
486
 
487
	n = strlen(prefix) + strlen(elem) + 2;
488
	p = emalloc(n);
489
	sprint(p, "%s/%s", prefix, elem);
490
	return p;
491
}
492
 
493
char *
494
strdup(char *s)
495
{
496
	char *t;
497
 
498
	t = emalloc(strlen(s) + 1);
499
	return strcpy(t, s);
500
}
501
 
502
void
503
setnames(File *f)
504
{
505
	sprint(newfile, "%s%s", newroot, f->new);
506
	if(f->old){
507
		if(f->old[0] == '/')
508
			sprint(oldfile, "%s%s", oldroot, f->old);
509
		else
510
			strcpy(oldfile, f->old);
511
	}else
512
		sprint(oldfile, "%s%s", oldroot, f->new);
513
	if(strlen(newfile) >= sizeof newfile 
514
	|| strlen(oldfile) >= sizeof oldfile)
515
		error("name overfile");
516
}
517
 
518
void
519
freefile(File *f)
520
{
521
	if(f->old)
522
		free(f->old);
523
	if(f->new)
524
		free(f->new);
525
	free(f);
526
}
527
 
528
/*
529
 * skip all files in the proto that
530
 * could be in the current dir
531
 */
532
void
533
skipdir(void)
534
{
535
	char *p, c;
536
	int level;
537
 
538
	if(indent < 0 || b == nil)	/* b is nil when copying adm/users */
539
		return;
540
	level = indent;
541
	for(;;){
542
		indent = 0;
543
		p = Brdline(b, '\n');
544
		lineno++;
545
		if(!p){
546
			indent = -1;
547
			return;
548
		}
549
		while((c = *p++) != '\n')
550
			if(c == ' ')
551
				indent++;
552
			else if(c == '\t')
553
				indent += 8;
554
			else
555
				break;
556
		if(indent <= level){
557
			Bseek(b, -Blinelen(b), 1);
558
			lineno--;
559
			return;
560
		}
561
	}
562
}
563
 
564
File*
565
getfile(File *old)
566
{
567
	File *f;
568
	char *elem;
569
	char *p;
570
	int c;
571
 
572
	if(indent < 0)
573
		return 0;
574
loop:
575
	indent = 0;
576
	p = Brdline(b, '\n');
577
	lineno++;
578
	if(!p){
579
		indent = -1;
580
		return 0;
581
	}
582
	while((c = *p++) != '\n')
583
		if(c == ' ')
584
			indent++;
585
		else if(c == '\t')
586
			indent += 8;
587
		else
588
			break;
589
	if(c == '\n' || c == '#')
590
		goto loop;
591
	p--;
592
	f = emalloc(sizeof *f);
593
	p = getname(p, &elem);
594
	if(debug)
595
		fprint(2, "getfile: %q root %q\n", elem, old->new);
596
	f->new = mkpath(old->new, elem);
597
	f->elem = utfrrune(f->new, L'/') + 1;
598
	p = getmode(p, &f->mode);
599
	p = getname(p, &f->uid);
600
	if(!*f->uid)
601
		f->uid = "-";
602
	p = getname(p, &f->gid);
603
	if(!*f->gid)
604
		f->gid = "-";
605
	f->old = getpath(p);
606
	if(f->old && strcmp(f->old, "-") == 0){
607
		free(f->old);
608
		f->old = 0;
609
	}
610
	setnames(f);
611
 
612
	if(debug)
613
		printfile(f);
614
 
615
	return f;
616
}
617
 
618
char*
619
getpath(char *p)
620
{
621
	char *q, *new;
622
	int c, n;
623
 
624
	while((c = *p) == ' ' || c == '\t')
625
		p++;
626
	q = p;
627
	while((c = *q) != '\n' && c != ' ' && c != '\t')
628
		q++;
629
	if(q == p)
630
		return 0;
631
	n = q - p;
632
	new = emalloc(n + 1);
633
	memcpy(new, p, n);
634
	new[n] = 0;
635
	return new;
636
}
637
 
638
char*
639
getname(char *p, char **buf)
640
{
641
	char *s, *start;
642
	int c;
643
 
644
	while((c = *p) == ' ' || c == '\t')
645
		p++;
646
 
647
	start = p;
648
	while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0')
649
		p++;
650
 
651
	*buf = malloc(p+1-start);
652
	if(*buf == nil)
653
		return nil;
654
	memmove(*buf, start, p-start);
655
	(*buf)[p-start] = '\0';
656
 
657
	if(**buf == '$'){
658
		s = getenv(*buf+1);
659
		if(s == 0){
660
			warn("can't read environment variable %q", *buf+1);
661
			skipdir();
662
			free(*buf);
663
			return nil;
664
		}
665
		free(*buf);
666
		*buf = s;
667
	}
668
	return p;
669
}
670
 
671
char*
672
getmode(char *p, ulong *xmode)
673
{
674
	char *buf, *s;
675
	ulong m;
676
 
677
	*xmode = ~0;
678
	p = getname(p, &buf);
679
	if(p == nil)
680
		return nil;
681
 
682
	s = buf;
683
	if(!*s || strcmp(s, "-") == 0)
684
		return p;
685
	m = 0;
686
	if(*s == 'd'){
687
		m |= DMDIR;
688
		s++;
689
	}
690
	if(*s == 'a'){
691
		m |= DMAPPEND;
692
		s++;
693
	}
694
	if(*s == 'l'){
695
		m |= DMEXCL;
696
		s++;
697
	}
698
	if(s[0] < '0' || s[0] > '7'
699
	|| s[1] < '0' || s[1] > '7'
700
	|| s[2] < '0' || s[2] > '7'
701
	|| s[3]){
702
		warn("bad mode specification %q", buf);
703
		free(buf);
704
		return p;
705
	}
706
	*xmode = m | strtoul(s, 0, 8);
707
	free(buf);
708
	return p;
709
}
710
 
711
void
712
setusers(void)
713
{
714
	File file;
715
	int m;
716
 
717
	if(fskind != Kfs)
718
		return;
719
	m = modes;
720
	modes = 1;
721
	file.uid = "adm";
722
	file.gid = "adm";
723
	file.mode = DMDIR|0775;
724
	file.new = "/adm";
725
	file.elem = "adm";
726
	file.old = 0;
727
	setnames(&file);
728
	strcpy(oldfile, file.new);	/* Don't use root for /adm */
729
	mkfile(&file);
730
	file.new = "/adm/users";
731
	file.old = users;
732
	file.elem = "users";
733
	file.mode = 0664;
734
	setnames(&file);
735
	if (file.old)
736
		strcpy(oldfile, file.old);	/* Don't use root for /adm/users */
737
	mkfile(&file);
738
	kfscmd("user");
739
	mkfile(&file);
740
	file.mode = DMDIR|0775;
741
	file.new = "/adm";
742
	file.old = "/adm";
743
	file.elem = "adm";
744
	setnames(&file);
745
	strcpy(oldfile, file.old);	/* Don't use root for /adm */
746
	mkfile(&file);
747
	modes = m;
748
}
749
 
750
void
751
mountkfs(char *name)
752
{
753
	char kname[64];
754
 
755
	if(fskind != Kfs)
756
		return;
757
	if(name[0])
758
		snprint(kname, sizeof kname, "/srv/kfs.%s", name);
759
	else
760
		strcpy(kname, "/srv/kfs");
761
	sfd = open(kname, ORDWR);
762
	if(sfd < 0){
763
		fprint(2, "can't open %q\n", kname);
764
		exits("open /srv/kfs");
765
	}
766
	if(mount(sfd, -1, "/n/kfs", MREPL|MCREATE, "") < 0){
767
		fprint(2, "can't mount kfs on /n/kfs\n");
768
		exits("mount kfs");
769
	}
770
	close(sfd);
771
	strcat(kname, ".cmd");
772
	sfd = open(kname, ORDWR);
773
	if(sfd < 0){
774
		fprint(2, "can't open %q\n", kname);
775
		exits("open /srv/kfs");
776
	}
777
}
778
 
779
void
780
kfscmd(char *cmd)
781
{
782
	char buf[4*1024];
783
	int n;
784
 
785
	if(fskind != Kfs)
786
		return;
787
	if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){
788
		fprint(2, "%q: error writing %q: %r", prog, cmd);
789
		return;
790
	}
791
	for(;;){
792
		n = read(sfd, buf, sizeof buf - 1);
793
		if(n <= 0)
794
			return;
795
		buf[n] = '\0';
796
		if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)
797
			return;
798
		if(strcmp(buf, "unknown command") == 0){
799
			fprint(2, "%q: command %q not recognized\n", prog, cmd);
800
			return;
801
		}
802
	}
803
}
804
 
805
void *
806
emalloc(ulong n)
807
{
808
	void *p;
809
 
810
	if((p = malloc(n)) == 0)
811
		error("out of memory");
812
	return p;
813
}
814
 
815
void
816
error(char *fmt, ...)
817
{
818
	char buf[1024];
819
	va_list arg;
820
 
821
	sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
822
	va_start(arg, fmt);
823
	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
824
	va_end(arg);
825
	fprint(2, "%s\n", buf);
826
	kfscmd("disallow");
827
	kfscmd("sync");
828
	exits(0);
829
}
830
 
831
void
832
warn(char *fmt, ...)
833
{
834
	char buf[1024];
835
	va_list arg;
836
 
837
	sprint(buf, "%q: %q:%d: ", prog, proto, lineno);
838
	va_start(arg, fmt);
839
	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
840
	va_end(arg);
841
	fprint(2, "%s\n", buf);
842
}
843
 
844
void
845
printfile(File *f)
846
{
847
	if(f->old)
848
		fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode);
849
	else
850
		fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);
851
}
852
 
853
void
854
usage(void)
855
{
856
	fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);
857
	exits("usage");
858
}