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