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