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