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 } 859