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
main(int argc,char ** argv)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
mkfs(File * me,int level)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
mktree(File * me,int rec)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
mkfile(File * f)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
copyfile(File * f,Dir * d,int permonly)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
uptodate(Dir * df,char * to)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
copy(Dir * d)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
mkdir(Dir * d)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
arch(Dir * d)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 *
mkpath(char * prefix,char * elem)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 *
strdup(char * s)494 strdup(char *s)
495 {
496 char *t;
497
498 t = emalloc(strlen(s) + 1);
499 return strcpy(t, s);
500 }
501
502 void
setnames(File * f)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
freefile(File * f)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
skipdir(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*
getfile(File * old)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*
getpath(char * p)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*
getname(char * p,char ** buf)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*
getmode(char * p,ulong * xmode)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
setusers(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
mountkfs(char * name)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
kfscmd(char * cmd)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 *
emalloc(ulong n)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
error(char * fmt,...)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
warn(char * fmt,...)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
printfile(File * f)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
usage(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 }
859