1implement Mkfs; 2 3include "sys.m"; 4 sys: Sys; 5 Dir, sprint, fprint: import sys; 6 7include "draw.m"; 8 9include "bufio.m"; 10 bufio: Bufio; 11 Iobuf: import bufio; 12 13include "string.m"; 14 str: String; 15 16include "arg.m"; 17 arg: Arg; 18 19Mkfs: module 20{ 21 init: fn(nil: ref Draw->Context, nil: list of string); 22}; 23 24LEN: con Sys->ATOMICIO; 25HUNKS: con 128; 26 27Kfs, Fs, Archive: con iota; # types of destination file sytems 28 29File: adt { 30 new: string; 31 elem: string; 32 old: string; 33 uid: string; 34 gid: string; 35 mode: int; 36}; 37 38b: ref Iobuf; 39bout: ref Iobuf; # stdout when writing archive 40newfile: string; 41oldfile: string; 42proto: string; 43cputype: string; 44users: string; 45oldroot: string; 46newroot: string; 47prog := "mkfs"; 48lineno := 0; 49buf: array of byte; 50zbuf: array of byte; 51buflen := 1024-8; 52indent: int; 53verb: int; 54modes: int; 55ream: int; 56debug: int; 57xflag: int; 58sfd: ref Sys->FD; 59fskind: int; # Kfs, Fs, Archive 60user: string; 61stderr: ref Sys->FD; 62usrid, grpid : string; 63setuid: int; 64 65init(nil: ref Draw->Context, args: list of string) 66{ 67 sys = load Sys Sys->PATH; 68 bufio = load Bufio Bufio->PATH; 69 str = load String String->PATH; 70 arg = load Arg Arg->PATH; 71 72 sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->FORKFD, nil); 73 74 stderr = sys->fildes(2); 75 if(arg == nil) 76 error(sys->sprint("can't load %q: %r", Arg->PATH)); 77 78 user = getuser(); 79 if(user == nil) 80 user = "none"; 81 name := ""; 82 file := ref File; 83 file.new = ""; 84 file.old = nil; 85 file.mode = 0; 86 oldroot = ""; 87 newroot = "/n/kfs"; 88 users = nil; 89 fskind = Kfs; # i suspect Inferno default should be different 90 arg->init(args); 91 arg->setusage("mkfs [-aprvxS] [-d root] [-n kfscmdname] [-s src-fs] [-u userfile] [-z n] [-G group] [-U user] proto ..."); 92 while((c := arg->opt()) != 0) 93 case c { 94 'a' => 95 fskind = Archive; 96 newroot = ""; 97 bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); 98 if(bout == nil) 99 error(sys->sprint("can't open standard output for archive: %r")); 100 'd' => 101 fskind = Fs; 102 newroot = arg->earg(); 103 'D' => 104 debug = 1; 105 'n' => 106 name = arg->earg(); 107 'p' => 108 modes = 1; 109 'q' => 110 ; 111 'r' => 112 ream = 1; 113 's' => 114 oldroot = arg->earg(); 115 'u' => 116 users = arg->earg(); 117 'v' => 118 verb = 1; 119 'x' => 120 xflag = 1; 121 'z' => 122 (buflen, nil) = str->toint(arg->earg(), 10); 123 buflen -= 8; # qid.path and tag at end of each kfs block 124 'U' => 125 usrid = arg->earg(); 126 'G' => 127 grpid = arg->earg(); 128 'S' => 129 setuid = 1; 130 * => 131 arg->usage(); 132 } 133 134 args = arg->argv(); 135 if(args == nil) 136 arg->usage(); 137 138 buf = array [buflen] of byte; 139 zbuf = array [buflen] of { * => byte 0 }; 140 141 if(name != nil) 142 openkfscmd(name); 143 kfscmd("allow"); 144 if(users != nil){ 145 proto = "users"; # for diagnostics 146 setusers(); 147 } 148 cputype = getenv("cputype"); 149 if(cputype == nil) 150 cputype = "dis"; 151 152 errs := 0; 153 for(; args != nil; args = tl args){ 154 proto = hd args; 155 fprint(stderr, "processing %s\n", proto); 156 157 b = bufio->open(proto, Sys->OREAD); 158 if(b == nil){ 159 fprint(stderr, "%s: can't open %q: %r: skipping\n", prog, proto); 160 errs++; 161 continue; 162 } 163 164 lineno = 0; 165 indent = 0; 166 mkfs(file, -1); 167 b.close(); 168 } 169 fprint(stderr, "file system made\n"); 170 kfscmd("disallow"); 171 kfscmd("sync"); 172 if(errs) 173 quit("skipped protos"); 174 if(fskind == Archive){ 175 bout.puts("end of archive\n"); 176 if(bout.flush() == Bufio->ERROR) 177 error(sys->sprint("write error: %r")); 178 } 179} 180 181quit(why: string) 182{ 183 if(bout != nil) 184 bout.flush(); 185 if(why != nil) 186 raise "fail:"+why; 187 exit; 188} 189 190mkfs(me: ref File, level: int) 191{ 192 (child, fp) := getfile(me); 193 if(child == nil) 194 return; 195 if(child.elem == "+" || child.elem == "*" || child.elem == "%"){ 196 rec := child.elem[0] == '+'; 197 filesonly := child.elem[0] == '%'; 198 child.new = me.new; 199 setnames(child); 200 mktree(child, rec, filesonly); 201 (child, fp) = getfile(me); 202 } 203 while(child != nil && indent > level){ 204 if(mkfile(child)) 205 mkfs(child, indent); 206 (child, fp) = getfile(me); 207 } 208 if(child != nil){ 209 b.seek(fp, 0); 210 lineno--; 211 } 212} 213 214mktree(me: ref File, rec: int, filesonly: int) 215{ 216 fd := sys->open(oldfile, Sys->OREAD); 217 if(fd == nil){ 218 warn(sys->sprint("can't open %q: %r", oldfile)); 219 return; 220 } 221 222 child := ref *me; 223 r := ref Rec(nil, 0); 224 for(;;){ 225 (n, d) := sys->dirread(fd); 226 if(n <= 0) 227 break; 228 for(i := 0; i < n; i++) 229 if (!recall(d[i].name, r)) { 230 if(filesonly && d[i].mode & Sys->DMDIR) 231 continue; 232 child.new = mkpath(me.new, d[i].name); 233 if(me.old != nil) 234 child.old = mkpath(me.old, d[i].name); 235 child.elem = d[i].name; 236 setnames(child); 237 if(copyfile(child, ref d[i], 1) && rec) 238 mktree(child, rec, filesonly); 239 } 240 } 241} 242 243# Recall namespace fix 244# -- remove duplicates (could use Readdir->init(,Readdir->COMPACT)) 245# obc 246 247Rec: adt 248{ 249 ad: array of string; 250 l: int; 251}; 252 253AL : con HUNKS; 254recall(e : string, r : ref Rec) : int 255{ 256 if (r.ad == nil) r.ad = array[AL] of string; 257 # double array 258 if (r.l >= len r.ad) { 259 nar := array[2*(len r.ad)] of string; 260 nar[0:] = r.ad; 261 r.ad = nar; 262 } 263 for(i := 0; i < r.l; i++) 264 if (r.ad[i] == e) return 1; 265 r.ad[r.l++] = e; 266 return 0; 267} 268 269mkfile(f: ref File): int 270{ 271 (i, dir) := sys->stat(oldfile); 272 if(i < 0){ 273 warn(sys->sprint("can't stat file %q: %r", oldfile)); 274 skipdir(); 275 return 0; 276 } 277 return copyfile(f, ref dir, 0); 278} 279 280copyfile(f: ref File, d: ref Dir, permonly: int): int 281{ 282 mode: int; 283 284 if(xflag && bout != nil){ 285 bout.puts(sys->sprint("%q\t%d\t%bd\n", f.new, d.mtime, d.length)); 286 return (d.mode & Sys->DMDIR) != 0; 287 } 288 d.name = f.elem; 289 if(d.dtype != 'M' && d.dtype != 'U'){ # hmm... Indeed! 290 d.uid = "inferno"; 291 d.gid = "inferno"; 292 mode = (d.mode >> 6) & 7; 293 d.mode |= mode | (mode << 3); 294 } 295 if(f.uid != "-") 296 d.uid = f.uid; 297 if(f.gid != "-") 298 d.gid = f.gid; 299 if(fskind == Fs && !setuid){ # new system: set to nil 300 d.uid = user; 301 d.gid = user; 302 } 303 if (usrid != nil) 304 d.uid = usrid; 305 if (grpid != nil) 306 d.gid = grpid; 307 if(f.mode != ~0){ 308 if(permonly) 309 d.mode = (d.mode & ~8r666) | (f.mode & 8r666); 310 else if((d.mode&Sys->DMDIR) != (f.mode&Sys->DMDIR)) 311 warn(sys->sprint("inconsistent mode for %s", f.new)); 312 else 313 d.mode = f.mode; 314 } 315 if(!uptodate(d, newfile)){ 316 if(d.mode & Sys->DMDIR) 317 mkdir(d); 318 else { 319 if(verb) 320 fprint(stderr, "%q\n", f.new); 321 copy(d); 322 } 323 }else if(modes){ 324 nd := sys->nulldir; 325 nd.mode = d.mode; 326 nd.mtime = d.mtime; 327 nd.gid = d.gid; 328 if(sys->wstat(newfile, nd) < 0) 329 warn(sys->sprint("can't set modes for %q: %r", f.new)); 330 # do the uid separately since different file systems object 331 nd = sys->nulldir; 332 nd.uid = d.uid; 333 sys->wstat(newfile, nd); 334 } 335 return (d.mode & Sys->DMDIR) != 0; 336} 337 338 339# check if file to is up to date with 340# respect to the file represented by df 341 342uptodate(df: ref Dir, newf: string): int 343{ 344 if(fskind == Archive || ream) 345 return 0; 346 (i, dt) := sys->stat(newf); 347 if(i < 0) 348 return 0; 349 return dt.mtime >= df.mtime; 350} 351 352copy(d: ref Dir) 353{ 354 t: ref Sys->FD; 355 n: int; 356 357 f := sys->open(oldfile, Sys->OREAD); 358 if(f == nil){ 359 warn(sys->sprint("can't open %q: %r", oldfile)); 360 return; 361 } 362 t = nil; 363 if(fskind == Archive) 364 arch(d); 365 else{ 366 (dname, fname) := str->splitr(newfile, "/"); 367 if(fname == nil) 368 error(sys->sprint("internal temporary file error (%s)", dname)); 369 cptmp := dname+"__mkfstmp"; 370 t = sys->create(cptmp, Sys->OWRITE, 8r666); 371 if(t == nil){ 372 warn(sys->sprint("can't create %q: %r", newfile)); 373 return; 374 } 375 } 376 377 for(tot := big 0;; tot += big n){ 378 n = sys->read(f, buf, buflen); 379 if(n < 0){ 380 warn(sys->sprint("can't read %q: %r", oldfile)); 381 break; 382 } 383 if(n == 0) 384 break; 385 if(fskind == Archive){ 386 if(bout.write(buf, n) != n) 387 error(sys->sprint("write error: %r")); 388 }else if(buf[0:buflen] == zbuf[0:buflen]){ 389 if(sys->seek(t, big buflen, 1) < big 0) 390 error(sys->sprint("can't write zeros to %q: %r", newfile)); 391 }else if(sys->write(t, buf, n) < n) 392 error(sys->sprint("can't write %q: %r", newfile)); 393 } 394 f = nil; 395 if(tot != d.length){ 396 warn(sys->sprint("wrong number bytes written to %s (was %bd should be %bd)", 397 newfile, tot, d.length)); 398 if(fskind == Archive){ 399 warn("seeking to proper position"); 400 bout.seek(d.length - tot, 1); 401 } 402 } 403 if(fskind == Archive) 404 return; 405 sys->remove(newfile); 406 nd := sys->nulldir; 407 nd.name = d.name; 408 nd.mode = d.mode; 409 nd.mtime = d.mtime; 410 if(sys->fwstat(t, nd) < 0) 411 error(sys->sprint("can't move tmp file to %q: %r", newfile)); 412 nd = sys->nulldir; 413 nd.gid = d.gid; 414 if(sys->fwstat(t, nd) < 0) 415 warn(sys->sprint("can't set group id of %q to %q: %r", newfile, d.gid)); 416 nd.gid = nil; 417 nd.uid = d.uid; 418 sys->fwstat(t, nd); 419} 420 421mkdir(d: ref Dir) 422{ 423 if(fskind == Archive){ 424 arch(d); 425 return; 426 } 427 fd := sys->create(newfile, Sys->OREAD, d.mode); 428 nd := sys->nulldir; 429 nd.mode = d.mode; 430 nd.gid = d.gid; 431 nd.mtime = d.mtime; 432 if(fd == nil){ 433 (i, d1) := sys->stat(newfile); 434 if(i < 0 || !(d1.mode & Sys->DMDIR)) 435 error(sys->sprint("can't create %q", newfile)); 436 if(sys->wstat(newfile, nd) < 0) 437 warn(sys->sprint("can't set modes for %q: %r", newfile)); 438 nd = sys->nulldir; 439 nd.uid = d.uid; 440 sys->wstat(newfile, nd); 441 return; 442 } 443 if(sys->fwstat(fd, nd) < 0) 444 warn(sys->sprint("can't set modes for %q: %r", newfile)); 445 nd = sys->nulldir; 446 nd.uid = d.uid; 447 sys->fwstat(fd, nd); 448} 449 450arch(d: ref Dir) 451{ 452 bout.puts(sys->sprint("%q %uo %q %q %ud %bd\n", 453 newfile, d.mode, d.uid, d.gid, d.mtime, d.length)); 454} 455 456mkpath(prefix, elem: string): string 457{ 458 return sys->sprint("%s/%s", prefix, elem); 459} 460 461setnames(f: ref File) 462{ 463 newfile = newroot+f.new; 464 if(f.old != nil){ 465 if(f.old[0] == '/') 466 oldfile = oldroot+f.old; 467 else 468 oldfile = f.old; 469 }else 470 oldfile = oldroot+f.new; 471} 472 473# 474# skip all files in the proto that 475# could be in the current dir 476# 477skipdir() 478{ 479 if(indent < 0) 480 return; 481 level := indent; 482 for(;;){ 483 indent = 0; 484 fp := b.offset(); 485 p := b.gets('\n'); 486 lineno++; 487 if(p == nil){ 488 indent = -1; 489 return; 490 } 491 for(j := 0; (c := p[j++]) != '\n';) 492 if(c == ' ') 493 indent++; 494 else if(c == '\t') 495 indent += 8; 496 else 497 break; 498 if(indent <= level){ 499 b.seek(fp, 0); 500 lineno--; 501 return; 502 } 503 } 504} 505 506getfile(old: ref File): (ref File, big) 507{ 508 f: ref File; 509 p, elem: string; 510 c: int; 511 512 if(indent < 0) 513 return (nil, big 0); 514 fp := b.offset(); 515 do { 516 indent = 0; 517 p = b.gets('\n'); 518 lineno++; 519 if(p == nil){ 520 indent = -1; 521 return (nil, big 0); 522 } 523 for(; (c = p[0]) != '\n'; p = p[1:]) 524 if(c == ' ') 525 indent++; 526 else if(c == '\t') 527 indent += 8; 528 else 529 break; 530 } while(c == '\n' || c == '#'); 531 f = ref File; 532 (elem, p) = getname(p); 533 if(debug) 534 fprint(stderr, "getfile: %q root %q\n", elem, old.new); 535 f.new = mkpath(old.new, elem); 536 (nil, f.elem) = str->splitr(f.new, "/"); 537 if(f.elem == nil) 538 error(sys->sprint("can't find file name component of %q", f.new)); 539 (f.mode, p) = getmode(p); 540 (f.uid, p) = getname(p); 541 if(f.uid == nil) 542 f.uid = "-"; 543 (f.gid, p) = getname(p); 544 if(f.gid == nil) 545 f.gid = "-"; 546 f.old = getpath(p); 547 if(f.old == "-") 548 f.old = nil; 549 setnames(f); 550 551 if(debug) 552 printfile(f); 553 554 return (f, fp); 555} 556 557getpath(p: string): string 558{ 559 for(i := 0; i < len p && (p[i] == ' ' || p[i] == '\t'); i++) 560 ; 561 for(n := i; n < len p && (c := p[n]) != '\n' && c != ' ' && c != '\t'; n++) 562 ; 563 return p[i:n]; 564} 565 566getname(p: string): (string, string) 567{ 568 for(i := 0; i < len p && (p[0] == ' ' || p[0] == '\t'); i++) 569 ; 570 s := ""; 571 quoted := 0; 572 for(; i < len p && (c := p[i]) != '\n' && (c != ' ' && c != '\t' || quoted); i++){ 573 if(c == '\''){ 574 if(i+1 >= len p || p[i+1] != '\''){ 575 quoted = !quoted; 576 continue; 577 } 578 i++; 579 } 580 s[len s] = c; 581 } 582 if(len s > 0 && s[0] == '$'){ 583 s = getenv(s[1:]); 584 if(s == nil) 585 error(sys->sprint("can't read environment variable %q", s)); 586 } 587 return (s, p[i:]); 588} 589 590getenv(s: string): string 591{ 592 if(s == "user") 593 return getuser(); 594 return readfile("/env/"+s); 595} 596 597getuser(): string 598{ 599 return readfile("/dev/user"); 600} 601 602readfile(f: string): string 603{ 604 fd := sys->open(f, Sys->OREAD); 605 if(fd != nil){ 606 a := array[256] of byte; 607 n := sys->read(fd, a, len a); 608 if(n > 0) 609 return string a[0:n]; 610 } 611 return nil; 612} 613 614getmode(p: string): (int, string) 615{ 616 s: string; 617 618 (s, p) = getname(p); 619 if(s == nil || s == "-") 620 return (~0, p); 621 os := s; 622 m := 0; 623 if(s[0] == 'd'){ 624 m |= Sys->DMDIR; 625 s = s[1:]; 626 } 627 if(s[0] == 'a'){ 628 m |= Sys->DMAPPEND; 629 s = s[1:]; 630 } 631 if(s[0] == 'l'){ 632 m |= Sys->DMEXCL; 633 s = s[1:]; 634 } 635 636 for(i:=0; i<len s || i < 3; i++) 637 if(i >= len s || !(s[i]>='0' && s[i]<='7')){ 638 warn(sys->sprint("bad mode specification %s", os)); 639 return (~0, p); 640 } 641 (v, nil) := str->toint(s, 8); 642 return (m|v, p); 643} 644 645setusers() 646{ 647 if(fskind != Kfs) 648 return; 649 file := ref File; 650 m := modes; 651 modes = 1; 652 file.uid = "adm"; 653 file.gid = "adm"; 654 file.mode = Sys->DMDIR|8r775; 655 file.new = "/adm"; 656 file.elem = "adm"; 657 file.old = nil; 658 setnames(file); 659 mkfile(file); 660 file.new = "/adm/users"; 661 file.old = users; 662 file.elem = "users"; 663 file.mode = 8r664; 664 setnames(file); 665 mkfile(file); 666 kfscmd("user"); 667 mkfile(file); 668 file.mode = Sys->DMDIR|8r775; 669 file.new = "/adm"; 670 file.old = "/adm"; 671 file.elem = "adm"; 672 setnames(file); 673 mkfile(file); 674 modes = m; 675} 676 677openkfscmd(name: string) 678{ 679 if(fskind != Kfs) 680 return; 681 kname := sys->sprint("/chan/kfs.%s.cmd", name); 682 sfd = sys->open(kname, Sys->ORDWR); 683 if(sfd == nil){ 684 fprint(stderr, "%s: can't open %q: %r\n", prog, kname); 685 quit("open kfscmd"); 686 } 687} 688 689kfscmd(cmd: string) 690{ 691 if(fskind != Kfs || sfd == nil) 692 return; 693 a := array of byte cmd; 694 if(sys->write(sfd, a, len a) != len a){ 695 fprint(stderr, "%s: error writing %s: %r", prog, cmd); 696 return; 697 } 698 for(;;){ 699 reply := array[4*1024] of byte; 700 n := sys->read(sfd, reply, len reply); 701 if(n <= 0) 702 return; 703 s := string reply[0:n]; 704 if(s == "done" || s == "success") 705 return; 706 if(s == "unknown command"){ 707 fprint(stderr, "%s: command %s not recognized\n", prog, cmd); 708 return; 709 } 710 } 711} 712 713error(s: string) 714{ 715 fprint(stderr, "%s: %s:%d: %s\n", prog, proto, lineno, s); 716 kfscmd("disallow"); 717 kfscmd("sync"); 718 quit("error"); 719} 720 721warn(s: string) 722{ 723 fprint(stderr, "%s: %s:%d: %s\n", prog, proto, lineno, s); 724} 725 726printfile(f: ref File) 727{ 728 if(f.old != nil) 729 fprint(stderr, "%q from %q %q %q %uo\n", f.new, f.old, f.uid, f.gid, f.mode); 730 else 731 fprint(stderr, "%q %q %q %uo\n", f.new, f.uid, f.gid, f.mode); 732} 733