1implement Kfs; 2 3# 4# Copyright © 1991-2003 Lucent Technologies Inc. 5# Limbo version Copyright © 2004 Vita Nuova Holdings Limited 6# 7 8# 9# TO DO: 10# - sync proc; Bmod; process structure 11# - swiz? 12 13include "sys.m"; 14 sys: Sys; 15 Qid, Dir: import Sys; 16 DMEXCL, DMAPPEND, DMDIR: import Sys; 17 QTEXCL, QTAPPEND, QTDIR: import Sys; 18 19include "draw.m"; 20 21include "styx.m"; 22 styx: Styx; 23 Tmsg, Rmsg: import styx; 24 NOFID, OEXEC, ORCLOSE, OREAD, OWRITE, ORDWR, OTRUNC: import Styx; 25 IOHDRSZ: import Styx; 26 27include "daytime.m"; 28 daytime: Daytime; 29 now: import daytime; 30 31include "arg.m"; 32 33Kfs: module 34{ 35 init: fn(nil: ref Draw->Context, nil: list of string); 36}; 37 38MAXBUFSIZE: con 16*1024; 39 40# 41# fundamental constants 42# 43NAMELEN: con 28; # size of names, including null byte 44NDBLOCK: con 6; # number of direct blocks in Dentry 45MAXFILESIZE: con big 16r7FFFFFFF; # Plan 9's limit (kfs's size is signed) 46 47SUPERADDR: con 1; 48ROOTADDR: con 2; 49 50QPDIR: con int (1<<31); 51QPNONE: con 0; 52QPROOT: con 1; 53QPSUPER: con 2; 54 55# 56# don't change, these are the mode bits on disc 57# 58DALLOC: con 16r8000; 59DDIR: con 16r4000; 60DAPND: con 16r2000; 61DLOCK: con 16r1000; 62DREAD: con 4; 63DWRITE: con 2; 64DEXEC: con 1; 65 66# 67# other constants 68# 69 70MINUTE: con 60; 71TLOCK: con 5*MINUTE; 72NTLOCK: con 200; # number of active file locks 73 74Buffering: con 1; 75 76FID1, FID2, FID3: con 1+iota; 77 78None: con 0; # user ID for "none" 79Noworld: con 9999; # conventional id for "noworld" group 80 81Lock: adt 82{ 83 c: chan of int; 84 new: fn(): ref Lock; 85 lock: fn(c: self ref Lock); 86 canlock: fn(c: self ref Lock): int; 87 unlock: fn(c: self ref Lock); 88}; 89 90Dentry: adt 91{ 92 name: string; 93 uid: int; 94 gid: int; 95 muid: int; # not set by plan 9's kfs 96 mode: int; # mode bits on disc: DALLOC etc 97 qid: Qid; # 9p1 format on disc 98 size: big; # only 32-bits on disc, and Plan 9 limits it to signed 99 atime: int; 100 mtime: int; 101 102 iob: ref Iobuf; # locked block containing directory entry, when in memory 103 buf: array of byte; # pointer into block to packed directory entry, when in memory 104 mod: int; # bits of buf that need updating 105 106 unpack: fn(a: array of byte): ref Dentry; 107 get: fn(p: ref Iobuf, slot: int): ref Dentry; 108 geta: fn(d: ref Device, addr: int, slot: int, qpath: int, mode: int): (ref Dentry, string); 109 getd: fn(f: ref File, mode: int): (ref Dentry, string); 110 put: fn(d: self ref Dentry); 111 access: fn(d: self ref Dentry, f: int, uid: int); 112 change: fn(d: self ref Dentry, f: int); 113 release: fn(d: self ref Dentry); 114 getblk: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; 115 getblk1: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; 116 rel2abs: fn(d: self ref Dentry, a: int, tag: int, putb: int): int; 117 trunc: fn(d: self ref Dentry, uid: int); 118 update: fn(d: self ref Dentry); 119 print: fn(d: self ref Dentry); 120}; 121 122Uname, Uids, Umode, Uqid, Usize, Utime: con 1<<iota; # Dentry.mod 123 124# 125# disc structure: 126# Tag: pad[2] tag[2] path[4] 127Tagsize: con 2+2+4; 128 129Tag: adt 130{ 131 tag: int; 132 path: int; 133 134 unpack: fn(a: array of byte): Tag; 135 pack: fn(t: self Tag, a: array of byte); 136}; 137 138Superb: adt 139{ 140 iob: ref Iobuf; 141 142 fstart: int; 143 fsize: int; 144 tfree: int; 145 qidgen: int; # generator for unique ids 146 147 fsok: int; 148 149 fbuf: array of byte; # nfree[4] free[FEPERBLK*4]; aliased into containing block 150 151 get: fn(dev: ref Device, flags: int): ref Superb; 152 touched: fn(s: self ref Superb); 153 put: fn(s: self ref Superb); 154 print: fn(s: self ref Superb); 155 156 pack: fn(s: self ref Superb, a: array of byte); 157 unpack: fn(a: array of byte): ref Superb; 158}; 159 160Device: adt 161{ 162 fd: ref Sys->FD; 163 ronly: int; 164 # could put locks here if necessary 165 # partitioning by ds(3) 166}; 167 168# 169# one for each locked qid 170# 171Tlock: adt 172{ 173 dev: ref Device; 174 time: int; 175 qpath: int; 176 file: cyclic ref File; # TO DO: probably not needed 177}; 178 179File: adt 180{ 181 qlock: chan of int; 182 qid: Qid; 183 wpath: ref Wpath; 184 tlock: cyclic ref Tlock; # if file is locked 185 fs: ref Device; 186 addr: int; 187 slot: int; 188 lastra: int; # read ahead address 189 fid: int; 190 uid: int; 191 open: int; 192 cons: int; # if opened by console 193 doffset: big; # directory reading 194 dvers: int; 195 dslot: int; 196 197 new: fn(fid: int): ref File; 198 access: fn(f: self ref File, d: ref Dentry, mode: int): int; 199 lock: fn(f: self ref File); 200 unlock: fn(f: self ref File); 201}; 202 203FREAD, FWRITE, FREMOV, FWSTAT: con 1<<iota; # File.open 204 205Chan: adt 206{ 207 fd: ref Sys->FD; # fd request came in on 208# rlock, wlock: QLock; # lock for reading/writing messages on cp 209 flags: int; 210 flist: list of ref File; # active files 211 fqlock: chan of int; 212# reflock: RWLock; # lock for Tflush 213 msize: int; # version 214 215 new: fn(fd: ref Sys->FD): ref Chan; 216 getfid: fn(c: self ref Chan, fid: int, flag: int): ref File; 217 putfid: fn(c: self ref Chan, f: ref File); 218 flock: fn(nil: self ref Chan); 219 funlock: fn(nil: self ref Chan); 220}; 221 222Hiob: adt 223{ 224 link: ref Iobuf; # TO DO: eliminate circular list 225 lk: ref Lock; 226 niob: int; 227 228 newbuf: fn(h: self ref Hiob): ref Iobuf; 229}; 230 231Iobuf: adt 232{ 233 qlock: chan of int; 234 dev: ref Device; 235 fore: cyclic ref Iobuf; # lru hash chain 236 back: cyclic ref Iobuf; # for lru 237 iobuf: array of byte; # only active while locked 238 xiobuf: array of byte; # "real" buffer pointer 239 addr: int; 240 flags: int; 241 242 get: fn(dev: ref Device, addr: int, flags: int):ref Iobuf; 243 put: fn(iob: self ref Iobuf); 244 lock: fn(iob: self ref Iobuf); 245 canlock: fn(iob: self ref Iobuf): int; 246 unlock: fn(iob: self ref Iobuf); 247 248 checktag: fn(iob: self ref Iobuf, tag: int, qpath: int): int; 249 settag: fn(iob: self ref Iobuf, tag: int, qpath: int); 250}; 251 252Wpath: adt 253{ 254 up: cyclic ref Wpath; # pointer upwards in path 255 addr: int; # directory entry addr 256 slot: int; # directory entry slot 257}; 258 259# 260# error codes generated from the file server 261# 262Eaccess: con "access permission denied"; 263Ealloc: con "phase error -- directory entry not allocated"; 264Eauth: con "authentication failed"; 265Eauthmsg: con "kfs: authentication not required"; 266Ebadspc: con "attach -- bad specifier"; 267Ebadu: con "attach -- privileged user"; 268Ebroken: con "close/read/write -- lock is broken"; 269Echar: con "bad character in directory name"; 270Econvert: con "protocol botch"; 271Ecount: con "read/write -- count too big"; 272Edir1: con "walk -- in a non-directory"; 273Edir2: con "create -- in a non-directory"; 274Edot: con "create -- . and .. illegal names"; 275Eempty: con "remove -- directory not empty"; 276Eentry: con "directory entry not found"; 277Eexist: con "create -- file exists"; 278Efid: con "unknown fid"; 279Efidinuse: con "fid already in use"; 280Efull: con "file system full"; 281Elocked: con "open/create -- file is locked"; 282Emode: con "open/create -- unknown mode"; 283Ename: con "create/wstat -- bad character in file name"; 284Enotd: con "wstat -- attempt to change directory"; 285Enotg: con "wstat -- not in group"; 286Enotl: con "wstat -- attempt to change length"; 287Enotm: con "wstat -- unknown type/mode"; 288Enotu: con "wstat -- not owner"; 289Eoffset: con "read/write -- offset negative"; 290Eopen: con "read/write -- on non open fid"; 291Ephase: con "phase error -- cannot happen"; 292Eqid: con "phase error -- qid does not match"; 293Eqidmode: con "wstat -- qid.qtype/dir.mode mismatch"; 294Eronly: con "file system read only"; 295Ersc: con "it's russ's fault. bug him."; 296Esystem: con "kfs system error"; 297Etoolong: con "name too long"; 298Etoobig: con "write -- file size limit"; 299Ewalk: con "walk -- too many (system wide)"; 300 301# 302# tags on block 303# 304Tnone, 305Tsuper, # the super block 306Tdir, # directory contents 307Tind1, # points to blocks 308Tind2, # points to Tind1 309Tfile, # file contents 310Tfree, # in free list 311Tbuck, # cache fs bucket 312Tvirgo, # fake worm virgin bits 313Tcache, # cw cache things 314MAXTAG: con iota; 315 316# 317# flags to Iobuf.get 318# 319 Bread, # read the block if miss 320 Bprobe, # return null if miss 321 Bmod, # set modified bit in buffer 322 Bimm, # set immediate bit in buffer 323 Bres: # never renamed 324 con 1<<iota; 325 326# 327# check flags 328# 329 Crdall, # read all files 330 Ctag, # rebuild tags 331 Cpfile, # print files 332 Cpdir, # print directories 333 Cfree, # rebuild free list 334 Cream, # clear all bad tags 335 Cbad, # clear all bad blocks 336 Ctouch, # touch old dir and indir 337 Cquiet: # report just nasty things 338 con 1<<iota; 339 340# 341# buffer size variables, determined by RBUFSIZE 342# 343RBUFSIZE: int; 344BUFSIZE: int; 345DIRPERBUF: int; 346INDPERBUF: int; 347INDPERBUF2: int; 348FEPERBUF: int; 349 350emptyblock: array of byte; 351 352wrenfd: ref Sys->FD; 353thedevice: ref Device; 354devnone: ref Device; 355wstatallow := 0; 356writeallow := 0; 357writegroup := 0; 358 359ream := 0; 360readonly := 0; 361noatime := 0; 362localfs: con 1; 363conschan: ref Chan; 364consuid := -1; 365consgid := -1; 366debug := 0; 367kfsname: string; 368consoleout: chan of string; 369mainlock: ref Lock; 370pids: list of int; 371 372noqid: Qid; 373 374init(nil: ref Draw->Context, args: list of string) 375{ 376 sys = load Sys Sys->PATH; 377 styx = load Styx Styx->PATH; 378 daytime = load Daytime Daytime->PATH; 379 380 styx->init(); 381 382 383 arg := load Arg Arg->PATH; 384 if(arg == nil) 385 error(sys->sprint("can't load %s: %r", Arg->PATH)); 386 arg->init(args); 387 arg->setusage("disk/kfs [-r [-b bufsize]] [-cADPRW] [-n name] kfsfile"); 388 bufsize := 1024; 389 nocheck := 0; 390 while((o := arg->opt()) != 0) 391 case o { 392 'c' => nocheck = 1; 393 'r' => ream = 1; 394 'b' => bufsize = int arg->earg(); 395 'D' => debug = !debug; 396 'P' => writeallow = 1; 397 'W' => wstatallow = 1; 398 'R' => readonly = 1; 399 'A' => noatime = 1; # mainly useful for flash 400 'n' => kfsname = arg->earg(); 401 * => arg->usage(); 402 } 403 args = arg->argv(); 404 if(args == nil) 405 arg->usage(); 406 arg = nil; 407 408 devnone = ref Device(nil, 1); 409 mainlock = Lock.new(); 410 411 conschan = Chan.new(nil); 412 conschan.msize = Styx->MAXRPC; 413 414 mode := Sys->ORDWR; 415 if(readonly) 416 mode = Sys->OREAD; 417 wrenfd = sys->open(hd args, mode); 418 if(wrenfd == nil) 419 error(sys->sprint("can't open %s: %r", hd args)); 420 thedevice = ref Device(wrenfd, readonly); 421 if(ream){ 422 if(bufsize <= 0 || bufsize % 512 || bufsize > MAXBUFSIZE) 423 error(sys->sprint("invalid block size %d", bufsize)); 424 RBUFSIZE = bufsize; 425 wrenream(thedevice); 426 }else{ 427 if(!wreninit(thedevice)) 428 error("kfs magic in trouble"); 429 } 430 BUFSIZE = RBUFSIZE - Tagsize; 431 DIRPERBUF = BUFSIZE / Dentrysize; 432 INDPERBUF = BUFSIZE / 4; 433 INDPERBUF2 = INDPERBUF * INDPERBUF; 434 FEPERBUF = (BUFSIZE - Super1size - 4) / 4; 435 emptyblock = array[RBUFSIZE] of {* => byte 0}; 436 437 iobufinit(30); 438 439 if(ream){ 440 superream(thedevice, SUPERADDR); 441 rootream(thedevice, ROOTADDR); 442 wstatallow = writeallow = 1; 443 } 444 if(wrencheck(wrenfd)) 445 error("kfs super/root in trouble"); 446 447 if(!ream && !readonly && !superok(0)){ 448 sys->print("kfs needs check\n"); 449 if(!nocheck) 450 check(thedevice, Cquiet|Cfree); 451 } 452 453 (d, e) := Dentry.geta(thedevice, ROOTADDR, 0, QPROOT, Bread); 454 if(d != nil && !(d.mode & DDIR)) 455 e = "not a directory"; 456 if(e != nil) 457 error("bad root: "+e); 458 if(debug) 459 d.print(); 460 d.put(); 461 462 sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); 463 464 sys->pctl(Sys->NEWFD, wrenfd.fd :: 0 :: 1 :: 2 :: nil); 465 wrenfd = sys->fildes(wrenfd.fd); 466 thedevice.fd = wrenfd; 467 468 c := chan of int; 469 470 if(Buffering){ 471 spawn syncproc(c); 472 pid := <-c; 473 if(pid) 474 pids = pid :: pids; 475 } 476 spawn consinit(c); 477 pid := <- c; 478 if(pid) 479 pids = pid :: pids; 480 481 spawn kfs(sys->fildes(0)); 482} 483 484error(s: string) 485{ 486 sys->fprint(sys->fildes(2), "kfs: %s\n", s); 487 for(; pids != nil; pids = tl pids) 488 kill(hd pids); 489 raise "fail:error"; 490} 491 492panic(s: string) 493{ 494 sys->fprint(sys->fildes(2), "kfs: panic: %s\n", s); 495 for(; pids != nil; pids = tl pids) 496 kill(hd pids); 497 raise "panic"; 498} 499 500syncproc(c: chan of int) 501{ 502 c <-= 0; 503} 504 505shutdown() 506{ 507 for(; pids != nil; pids = tl pids) 508 kill(hd pids); 509 # TO DO: when Bmod deferred, must sync 510 # sync super block 511 if(!readonly && superok(1)){ 512 # ; 513 } 514 iobufclear(); 515} 516 517kill(pid: int) 518{ 519 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 520 if(fd != nil) 521 sys->fprint(fd, "kill"); 522} 523 524# 525# limited file system support for console 526# 527kattach(fid: int): string 528{ 529 return applycons(ref Tmsg.Attach(1, fid, NOFID, "adm", "")).t1; 530} 531 532kopen(oldfid: int, newfid: int, names: array of string, mode: int): string 533{ 534 (r1, e1) := applycons(ref Tmsg.Walk(1, oldfid, newfid, names)); 535 if(r1 != nil){ 536 pick m := r1 { 537 Walk => 538 if(len m.qids != len names){ 539 kclose(newfid); 540 cprint(Eexist); 541 return Eexist; 542 } 543 * => 544 return "unexpected reply"; 545 } 546 (r1, e1) = applycons(ref Tmsg.Open(1, newfid, mode)); 547 if(e1 != nil){ 548 kclose(newfid); 549 cprint(sys->sprint("open: %s", e1)); 550 } 551 } 552 return e1; 553} 554 555kread(fid: int, offset: int, nbytes: int): (array of byte, string) 556{ 557 (r, e) := applycons(ref Tmsg.Read(1, fid, big offset, nbytes)); 558 if(r != nil){ 559 pick m := r { 560 Read => 561 return (m.data, nil); 562 * => 563 return (nil, "unexpected reply"); 564 } 565 } 566 cprint(sys->sprint("read error: %s", e)); 567 return (nil, e); 568} 569 570kclose(fid: int) 571{ 572 applycons(ref Tmsg.Clunk(1, fid)); 573} 574 575applycons(t: ref Tmsg): (ref Rmsg, string) 576{ 577 r := apply(conschan, t); 578 pick m := r { 579 Error => 580 if(debug) 581 cprint(sys->sprint("%s: %s\n", t.text(), m.ename)); 582 return (nil, m.ename); 583 } 584 return (r, nil); 585} 586 587# 588# always reads /adm/users in userinit(), then 589# optionally serves the command file, if used. 590# 591Req: adt { 592 nbytes: int; 593 rc: chan of (array of byte, string); 594}; 595 596consinit(c: chan of int) 597{ 598 kattach(FID1); 599 userinit(); 600 if(kfsname == nil){ 601 c <-= 0; 602 exit; 603 } 604 cfname := "kfs."+kfsname+".cmd"; 605 sys->bind("#s", "/chan", Sys->MBEFORE); 606 file := sys->file2chan("/chan", cfname); 607 if(file == nil) 608 error(sys->sprint("can't create /chan/%s: %r", cfname)); 609 c <-= sys->pctl(0, nil); 610 consc := chan of string; 611 checkend := chan of int; 612 cdata: array of byte; 613 pending: ref Req; 614 cfid := -1; 615 for(;;) alt{ 616 (nil, nbytes, fid, rc) := <-file.read => 617 if(rc == nil) 618 break; 619 if(cfid == -1) 620 cfid = fid; 621 if(fid != cfid || pending != nil){ 622 rc <-= (nil, "kfs.cmd is busy"); 623 break; 624 } 625 if(cdata != nil){ 626 cdata = reply(rc, nbytes, cdata); 627 break; 628 } 629 if(nbytes <= 0 || consoleout == nil){ 630 rc <-= (nil, nil); 631 break; 632 } 633 pending = ref Req(nbytes, rc); 634 consc = consoleout; 635 (nil, data, fid, wc) := <-file.write => 636 if(cfid == -1) 637 cfid = fid; 638 if(wc == nil){ 639 if(fid == cfid){ 640 cfid = -1; 641 pending = nil; 642 cdata = nil; # discard unread data from last command 643 if((consc = consoleout) == nil) 644 consc = chan of string; 645 } 646 break; 647 } 648 if(fid != cfid){ 649 wc <-= (0, "kfs.cmd is busy"); 650 break; 651 } 652 (nf, fld) := sys->tokenize(string data, " \t\n\r"); 653 if(nf < 1){ 654 wc <-= (0, "illegal kfs request"); 655 break; 656 } 657 case hd fld { 658 "check" => 659 if(consoleout != nil){ 660 wc <-= (0, "check in progress"); 661 break; 662 } 663 f := 0; 664 if(nf > 1){ 665 f = checkflags(hd tl fld); 666 if(f < 0){ 667 wc <-= (0, "illegal check flag: "+hd tl fld); 668 break; 669 } 670 } 671 consoleout = chan of string; 672 spawn checkproc(checkend, f); 673 wc <-= (len data, nil); 674 consc = consoleout; 675 "users" or "user" => 676 cmd_users(); 677 wc <-= (len data, nil); 678 "sync" => 679 # nothing TO DO until writes are buffered 680 wc <-= (len data, nil); 681 "allow" => 682 wstatallow = writeallow = 1; 683 wc <-= (len data, nil); 684 "allowoff" or "disallow" => 685 wstatallow = writeallow = 0; 686 wc <-= (len data, nil); 687 * => 688 wc <-= (0, "unknown kfs request"); 689 continue; 690 } 691 <-checkend => 692 consoleout = nil; 693 consc = chan of string; 694 s := <-consc => 695 #sys->print("<-%s\n", s); 696 req := pending; 697 pending = nil; 698 if(req != nil) 699 cdata = reply(req.rc, req.nbytes, array of byte s); 700 else 701 cdata = array of byte s; 702 if(cdata != nil && cfid != -1) 703 consc = chan of string; 704 } 705} 706 707reply(rc: chan of (array of byte, string), nbytes: int, a: array of byte): array of byte 708{ 709 if(len a < nbytes) 710 nbytes = len a; 711 rc <-= (a[0:nbytes], nil); 712 if(nbytes == len a) 713 return nil; 714 return a[nbytes:]; 715} 716 717checkproc(c: chan of int, flags: int) 718{ 719 mainlock.lock(); 720 check(thedevice, flags); 721 mainlock.unlock(); 722 c <-= 1; 723} 724 725# 726# normal kfs service 727# 728kfs(rfd: ref Sys->FD) 729{ 730 cp := Chan.new(rfd); 731 while((t := Tmsg.read(rfd, cp.msize)) != nil){ 732 if(debug) 733 sys->print("<- %s\n", t.text()); 734 r := apply(cp, t); 735 pick m := r { 736 Error => 737 r.tag = t.tag; 738 } 739 if(debug) 740 sys->print("-> %s\n", r.text()); 741 rbuf := r.pack(); 742 if(rbuf == nil) 743 panic("Rmsg.pack"); 744 if(sys->write(rfd, rbuf, len rbuf) != len rbuf) 745 panic("mount write"); 746 } 747 shutdown(); 748} 749 750apply(cp: ref Chan, t: ref Tmsg): ref Rmsg 751{ 752 mainlock.lock(); # TO DO: this is just to keep console and kfs from colliding 753 r: ref Rmsg; 754 pick m := t { 755 Readerror => 756 error(sys->sprint("mount read error: %s", m.error)); 757 Version => 758 r = rversion(cp, m); 759 Auth => 760 r = rauth(cp, m); 761 Flush => 762 r = rflush(cp, m); 763 Attach => 764 r = rattach(cp, m); 765 Walk => 766 r = rwalk(cp, m); 767 Open => 768 r = ropen(cp, m); 769 Create => 770 r = rcreate(cp, m); 771 Read => 772 r = rread(cp, m); 773 Write => 774 r = rwrite(cp, m); 775 Clunk => 776 r = rclunk(cp, m); 777 Remove => 778 r = rremove(cp, m); 779 Stat => 780 r = rstat(cp, m); 781 Wstat => 782 r = rwstat(cp, m); 783 * => 784 panic("Styx mtype"); 785 return nil; 786 } 787 mainlock.unlock(); 788 return r; 789} 790 791rversion(cp: ref Chan, t: ref Tmsg.Version): ref Rmsg 792{ 793 cp.msize = RBUFSIZE+IOHDRSZ; 794 if(cp.msize < Styx->MAXRPC) 795 cp.msize = Styx->MAXRPC; 796 (msize, version) := styx->compatible(t, Styx->MAXRPC, Styx->VERSION); 797 if(msize < 256) 798 return ref Rmsg.Error(t.tag, "message size too small"); 799 return ref Rmsg.Version(t.tag, msize, version); 800} 801 802rauth(nil: ref Chan, t: ref Tmsg.Auth): ref Rmsg 803{ 804 return ref Rmsg.Error(t.tag, Eauthmsg); 805} 806 807rflush(nil: ref Chan, t: ref Tmsg.Flush): ref Rmsg 808{ 809 # runlock(cp.reflock); 810 # wlock(cp.reflock); 811 # wunlock(cp.reflock); 812 # rlock(cp.reflock); 813 return ref Rmsg.Flush(t.tag); 814} 815 816err(t: ref Tmsg, s: string): ref Rmsg.Error 817{ 818 return ref Rmsg.Error(t.tag, s); 819} 820 821ferr(t: ref Tmsg, s: string, file: ref File, p: ref Iobuf): ref Rmsg.Error 822{ 823 if(p != nil) 824 p.put(); 825 if(file != nil) 826 file.unlock(); 827 return ref Rmsg.Error(t.tag, s); 828} 829 830File.new(fid: int): ref File 831{ 832 f := ref File; 833 f.qlock = chan[1] of int; 834 f.fid = fid; 835 f.cons = 0; 836 f.tlock = nil; 837 f.wpath = nil; 838 f.doffset = big 0; 839 f.dvers = 0; 840 f.dslot = 0; 841 f.uid = None; 842 f.cons = 0; 843# f.cuid = None; 844 return f; 845} 846 847# 848# returns a locked file structure 849# 850 851Chan.getfid(cp: self ref Chan, fid: int, flag: int): ref File 852{ 853 if(fid == NOFID) 854 return nil; 855 cp.flock(); 856 for(l := cp.flist; l != nil; l = tl l){ 857 f := hd l; 858 if(f.fid == fid){ 859 cp.funlock(); 860 if(flag) 861 return nil; # fid in use 862 f.lock(); 863 if(f.fid == fid) 864 return f; 865 f.unlock(); 866 cp.flock(); 867 } 868 } 869 if(flag == 0){ 870 sys->print("kfs: cannot find %H.%ud", cp, fid); 871 cp.funlock(); 872 return nil; 873 } 874 f := File.new(fid); 875 f.lock(); 876 cp.flist = f :: cp.flist; 877 cp.funlock(); 878 return f; 879} 880 881Chan.putfid(cp: self ref Chan, f: ref File) 882{ 883 cp.flock(); 884 nl: list of ref File; 885 for(x := cp.flist; x != nil; x = tl x) 886 if(hd x != f) 887 nl = hd x :: nl; 888 cp.flist = nl; 889 cp.funlock(); 890 f.unlock(); 891} 892 893File.lock(f: self ref File) 894{ 895 f.qlock <-= 1; 896} 897 898File.unlock(f: self ref File) 899{ 900 <-f.qlock; 901} 902 903Chan.new(fd: ref Sys->FD): ref Chan 904{ 905 c := ref Chan; 906 c.fd = fd; 907 c.fqlock = chan[1] of int; 908# rlock, wlock: QLock; # lock for reading/writing messages on cp 909 c.flags = 0; 910# reflock: RWLock; # lock for Tflush 911 c.msize = 0; # set by rversion 912 return c; 913} 914 915Chan.flock(c: self ref Chan) 916{ 917 c.fqlock <-= 1; 918} 919 920Chan.funlock(c: self ref Chan) 921{ 922 <-c.fqlock; 923} 924 925rattach(cp: ref Chan, t: ref Tmsg.Attach): ref Rmsg 926{ 927 if(t.aname != "" && t.aname != "main") 928 return err(t, Ebadspc); 929 file := cp.getfid(t.fid, 1); 930 if(file == nil) 931 return err(t, Efidinuse); 932 p := Iobuf.get(thedevice, ROOTADDR, Bread); 933 if(p == nil){ 934 cp.putfid(file); 935 return err(t, "can't access root block"); 936 } 937 d := Dentry.get(p, 0); 938 if(d == nil || p.checktag(Tdir, QPROOT) || (d.mode & DALLOC) == 0 || (d.mode & DDIR) == 0){ 939 p.put(); 940 cp.putfid(file); 941 return err(t, Ealloc); 942 } 943 if(file.access(d, DEXEC)){ 944 p.put(); 945 cp.putfid(file); 946 return err(t, Eaccess); 947 } 948 d.access(FREAD, file.uid); 949 file.fs = thedevice; 950 file.qid = d.qid; 951 file.addr = p.addr; 952 file.slot = 0; 953 file.open = 0; 954 file.uid = strtouid(t.uname); 955 file.wpath = nil; 956 p.put(); 957 qid := file.qid; 958 file.unlock(); 959 return ref Rmsg.Attach(t.tag, qid); 960} 961 962clone(nfile: ref File, file: ref File) 963{ 964 nfile.qid = file.qid; 965 nfile.wpath = file.wpath; 966 nfile.fs = file.fs; 967 nfile.addr = file.addr; 968 nfile.slot = file.slot; 969 nfile.uid = file.uid; 970# nfile.cuid = None; 971 nfile.open = file.open & ~FREMOV; 972} 973 974walkname(file: ref File, wname: string): (string, Qid) 975{ 976 # 977 # File must not have been opened for I/O by an open 978 # or create message and must represent a directory. 979 # 980 if(file.open != 0) 981 return (Emode, noqid); 982 983 (d, e) := Dentry.getd(file, Bread); 984 if(d == nil) 985 return (e, noqid); 986 if(!(d.mode & DDIR)){ 987 d.put(); 988 return (Edir1, noqid); 989 } 990 991 # 992 # For walked elements the implied user must 993 # have permission to search the directory. 994 # 995 if(file.access(d, DEXEC)){ 996 d.put(); 997 return (Eaccess, noqid); 998 } 999 d.access(FREAD, file.uid); 1000 1001 if(wname == "." || wname == ".." && file.wpath == nil){ 1002 d.put(); 1003 return (nil, file.qid); 1004 } 1005 1006 d1: ref Dentry; # entry for wname, if found 1007 slot: int; 1008 1009 if(wname == ".."){ 1010 d.put(); 1011 addr := file.wpath.addr; 1012 slot = file.wpath.slot; 1013 (d1, e) = Dentry.geta(file.fs, addr, slot, QPNONE, Bread); 1014 if(d1 == nil) 1015 return (e, noqid); 1016 file.wpath = file.wpath.up; 1017 }else{ 1018 1019 Search: 1020 for(addr := 0; ; addr++){ 1021 if(d.iob == nil){ 1022 (d, e) = Dentry.getd(file, Bread); 1023 if(d == nil) 1024 return (e, noqid); 1025 } 1026 p1 := d.getblk1(addr, 0); 1027 if(p1 == nil || p1.checktag(Tdir, int d.qid.path)){ 1028 if(p1 != nil) 1029 p1.put(); 1030 return (Eentry, noqid); 1031 } 1032 for(slot = 0; slot < DIRPERBUF; slot++){ 1033 d1 = Dentry.get(p1, slot); 1034 if(!(d1.mode & DALLOC)) 1035 continue; 1036 if(wname != d1.name) 1037 continue; 1038 # 1039 # update walk path 1040 # 1041 file.wpath = ref Wpath(file.wpath, file.addr, file.slot); 1042 slot += DIRPERBUF*addr; 1043 break Search; 1044 } 1045 p1.put(); 1046 } 1047 d.put(); 1048 } 1049 1050 file.addr = d1.iob.addr; 1051 file.slot = slot; 1052 file.qid = d1.qid; 1053 d1.put(); 1054 return (nil, file.qid); 1055} 1056 1057rwalk(cp: ref Chan, t: ref Tmsg.Walk): ref Rmsg 1058{ 1059 nfile, tfile: ref File; 1060 q: Qid; 1061 1062 # The file identified by t.fid must be valid in the 1063 # current session and must not have been opened for I/O 1064 # by an open or create message. 1065 1066 if((file := cp.getfid(t.fid, 0)) == nil) 1067 return err(t, Efid); 1068 if(file.open != 0) 1069 return ferr(t, Emode, file, nil); 1070 1071 # If newfid is not the same as fid, allocate a new file; 1072 # a side effect is checking newfid is not already in use (error); 1073 # if there are no names to walk this will be equivalent to a 1074 # simple 'clone' operation. 1075 # Otherwise, fid and newfid are the same and if there are names 1076 # to walk make a copy of 'file' to be used during the walk as 1077 # 'file' must only be updated on success. 1078 # Finally, it's a no-op if newfid is the same as fid and t.nwname 1079 # is 0. 1080 1081 nwqid := 0; 1082 if(t.newfid != t.fid){ 1083 if((nfile = cp.getfid(t.newfid, 1)) == nil) 1084 return ferr(t, Efidinuse, file, nil); 1085 } 1086 else if(len t.names != 0) 1087 nfile = tfile = File.new(NOFID); 1088 else{ 1089 file.unlock(); 1090 return ref Rmsg.Walk(t.tag, nil); 1091 } 1092 clone(nfile, file); 1093 1094 r := ref Rmsg.Walk(t.tag, array[len t.names] of Qid); 1095 error: string; 1096 for(nwname := 0; nwname < len t.names; nwname++){ 1097 (error, q) = walkname(nfile, t.names[nwname]); 1098 if(error != nil) 1099 break; 1100 r.qids[nwqid++] = q; 1101 } 1102 1103 if(len t.names == 0){ 1104 1105 # Newfid must be different to fid (see above) 1106 # so this is a simple 'clone' operation - there's 1107 # nothing to do except unlock unless there's 1108 # an error. 1109 1110 nfile.unlock(); 1111 if(error != nil) 1112 cp.putfid(nfile); 1113 }else if(nwqid < len t.names){ 1114 # 1115 # Didn't walk all elements, 'clunk' nfile 1116 # and leave 'file' alone. 1117 # Clear error if some of the elements were 1118 # walked OK. 1119 # 1120 if(nfile != tfile) 1121 cp.putfid(nfile); 1122 if(nwqid != 0) 1123 error = nil; 1124 r.qids = r.qids[0:nwqid]; 1125 }else{ 1126 # 1127 # Walked all elements. If newfid is the same 1128 # as fid must update 'file' from the temporary 1129 # copy used during the walk. 1130 # Otherwise just unlock (when using tfile there's 1131 # no need to unlock as it's a local). 1132 # 1133 if(nfile == tfile){ 1134 file.qid = nfile.qid; 1135 file.wpath = nfile.wpath; 1136 file.addr = nfile.addr; 1137 file.slot = nfile.slot; 1138 }else 1139 nfile.unlock(); 1140 } 1141 file.unlock(); 1142 1143 if(error != nil) 1144 return err(t, error); 1145 return r; 1146} 1147 1148ropen(cp: ref Chan, f: ref Tmsg.Open): ref Rmsg 1149{ 1150 wok := cp == conschan || writeallow; 1151 1152 if((file := cp.getfid(f.fid, 0)) == nil) 1153 return err(f, Efid); 1154 1155 # 1156 # if remove on close, check access here 1157 # 1158 ro := isro(file.fs) || (writegroup && !ingroup(file.uid, writegroup)); 1159 if(f.mode & ORCLOSE){ 1160 if(ro) 1161 return ferr(f, Eronly, file, nil); 1162 # 1163 # check on parent directory of file to be deleted 1164 # 1165 if(file.wpath == nil || file.wpath.addr == file.addr) 1166 return ferr(f, Ephase, file, nil); 1167 p := Iobuf.get(file.fs, file.wpath.addr, Bread); 1168 if(p == nil || p.checktag(Tdir, QPNONE)) 1169 return ferr(f, Ephase, file, p); 1170 if((d := Dentry.get(p, file.wpath.slot)) == nil || !(d.mode & DALLOC)) 1171 return ferr(f, Ephase, file, p); 1172 if(file.access(d, DWRITE)) 1173 return ferr(f, Eaccess, file, p); 1174 p.put(); 1175 } 1176 (d, e) := Dentry.getd(file, Bread); 1177 if(d == nil) 1178 return ferr(f, e, file, nil); 1179 p := d.iob; 1180 qid := d.qid; 1181 fmod: int; 1182 case f.mode & 7 { 1183 1184 OREAD => 1185 if(file.access(d, DREAD) && !wok) 1186 return ferr(f, Eaccess, file, p); 1187 fmod = FREAD; 1188 1189 OWRITE => 1190 if((d.mode & DDIR) || (file.access(d, DWRITE) && !wok)) 1191 return ferr(f, Eaccess, file, p); 1192 if(ro) 1193 return ferr(f, Eronly, file, p); 1194 fmod = FWRITE; 1195 1196 ORDWR => 1197 if((d.mode & DDIR) 1198 || (file.access(d, DREAD) && !wok) 1199 || (file.access(d, DWRITE) && !wok)) 1200 return ferr(f, Eaccess, file, p); 1201 if(ro) 1202 return ferr(f, Eronly, file, p); 1203 fmod = FREAD+FWRITE; 1204 1205 OEXEC => 1206 if((d.mode & DDIR) || (file.access(d, DEXEC) && !wok)) 1207 return ferr(f, Eaccess, file, p); 1208 fmod = FREAD; 1209 1210 * => 1211 return ferr(f, Emode, file, p); 1212 } 1213 if(f.mode & OTRUNC){ 1214 if((d.mode & DDIR) || (file.access(d, DWRITE) && !wok)) 1215 return ferr(f, Eaccess, file, p); 1216 if(ro) 1217 return ferr(f, Eronly, file, p); 1218 } 1219 if(d.mode & DLOCK){ 1220 if((t := tlocked(file, d)) == nil) 1221 return ferr(f, Elocked, file, p); 1222 file.tlock = t; 1223 t.file = file; 1224 } 1225 if(f.mode & ORCLOSE) 1226 fmod |= FREMOV; 1227 file.open = fmod; 1228 if((f.mode & OTRUNC) && !(d.mode & DAPND)){ 1229 d.trunc(file.uid); 1230 qid.vers = d.qid.vers; 1231 } 1232 file.lastra = 1; 1233 p.put(); 1234 file.unlock(); 1235 return ref Rmsg.Open(f.tag, qid, cp.msize-IOHDRSZ); 1236} 1237 1238rcreate(cp: ref Chan, f: ref Tmsg.Create): ref Rmsg 1239{ 1240 wok := cp == conschan || writeallow; 1241 1242 if((file := cp.getfid(f.fid, 0)) == nil) 1243 return err(f, Efid); 1244 if(isro(file.fs) || (writegroup && !ingroup(file.uid, writegroup))) 1245 return ferr(f, Eronly, file, nil); 1246 1247 (d, e) := Dentry.getd(file, Bread); 1248 if(e != nil) 1249 return ferr(f, e, file, nil); 1250 p := d.iob; 1251 if(!(d.mode & DDIR)) 1252 return ferr(f, Edir2, file, p); 1253 if(file.access(d, DWRITE) && !wok) 1254 return ferr(f, Eaccess, file, p); 1255 d.access(FREAD, file.uid); 1256 1257 # 1258 # Check the name is valid and will fit in an old 1259 # directory entry. 1260 # 1261 if((l := checkname9p2(f.name)) == 0) 1262 return ferr(f, Ename, file, p); 1263 if(l+1 > NAMELEN) 1264 return ferr(f, Etoolong, file, p); 1265 if(f.name == "." || f.name == "..") 1266 return ferr(f, Edot, file, p); 1267 1268 addr1 := 0; # block with first empty slot, if any 1269 slot1 := 0; 1270 for(addr := 0; ; addr++){ 1271 if((p1 := d.getblk(addr, 0)) == nil){ 1272 if(addr1 != 0) 1273 break; 1274 p1 = d.getblk(addr, Tdir); 1275 } 1276 if(p1 == nil) 1277 return ferr(f, Efull, file, p); 1278 if(p1.checktag(Tdir, int d.qid.path)){ 1279 p1.put(); 1280 return ferr(f, Ephase, file, p); 1281 } 1282 for(slot := 0; slot < DIRPERBUF; slot++){ 1283 d1 := Dentry.get(p1, slot); 1284 if(!(d1.mode & DALLOC)){ 1285 if(addr1 == 0){ 1286 addr1 = p1.addr; 1287 slot1 = slot + addr*DIRPERBUF; 1288 } 1289 continue; 1290 } 1291 if(f.name == d1.name){ 1292 p1.put(); 1293 return ferr(f, Eexist, file, p); 1294 } 1295 } 1296 p1.put(); 1297 } 1298 1299 fmod: int; 1300 1301 case f.mode & 7 { 1302 OEXEC or 1303 OREAD => # seems only useful to make directories 1304 fmod = FREAD; 1305 1306 OWRITE => 1307 fmod = FWRITE; 1308 1309 ORDWR => 1310 fmod = FREAD+FWRITE; 1311 1312 * => 1313 return ferr(f, Emode, file, p); 1314 } 1315 if(f.perm & DMDIR) 1316 if((f.mode & OTRUNC) || (f.perm & DMAPPEND) || (fmod & FWRITE)) 1317 return ferr(f, Eaccess, file, p); 1318 1319 # do it 1320 1321 path := qidpathgen(file.fs); 1322 if((p1 := Iobuf.get(file.fs, addr1, Bread|Bimm|Bmod)) == nil) 1323 return ferr(f, Ephase, file, p); 1324 d1 := Dentry.get(p1, slot1); 1325 if(d1 == nil || p1.checktag(Tdir, int d.qid.path)){ 1326 p.put(); 1327 return ferr(f, Ephase, file, p1); 1328 } 1329 if(d1.mode & DALLOC){ 1330 p.put(); 1331 return ferr(f, Ephase, file, p1); 1332 } 1333 1334 d1.name = f.name; 1335 if(cp == conschan){ 1336 d1.uid = consuid; 1337 d1.gid = consgid; 1338 } 1339 else{ 1340 d1.uid = file.uid; 1341 d1.gid = d.gid; 1342 f.perm &= d.mode | ~8r666; 1343 if(f.perm & DMDIR) 1344 f.perm &= d.mode | ~8r777; 1345 } 1346 d1.qid.path = big path; 1347 d1.qid.vers = 0; 1348 d1.mode = DALLOC | (f.perm & 8r777); 1349 if(f.perm & DMDIR) 1350 d1.mode |= DDIR; 1351 if(f.perm & DMAPPEND) 1352 d1.mode |= DAPND; 1353 t: ref Tlock; 1354 if(f.perm & DMEXCL){ 1355 d1.mode |= DLOCK; 1356 t = tlocked(file, d1); 1357 # if nil, out of tlock structures 1358 } 1359 d1.access(FWRITE, file.uid); 1360 d1.change(~0); 1361 d1.update(); 1362 qid := mkqid(path, 0, d1.mode); 1363 p1.put(); 1364 d.change(~0); 1365 d.access(FWRITE, file.uid); 1366 d.update(); 1367 p.put(); 1368 1369 # 1370 # do a walk to new directory entry 1371 # 1372 file.wpath = ref Wpath(file.wpath, file.addr, file.slot); 1373 file.qid = qid; 1374 file.tlock = t; 1375 if(t != nil) 1376 t.file = file; 1377 file.lastra = 1; 1378 if(f.mode & ORCLOSE) 1379 fmod |= FREMOV; 1380 file.open = fmod; 1381 file.addr = addr1; 1382 file.slot = slot1; 1383 file.unlock(); 1384 return ref Rmsg.Create(f.tag, qid, cp.msize-IOHDRSZ); 1385} 1386 1387dirread(cp: ref Chan, f: ref Tmsg.Read, file: ref File, d: ref Dentry): ref Rmsg 1388{ 1389 p1: ref Iobuf; 1390 d1: ref Dentry; 1391 1392 count := f.count; 1393 data := array[count] of byte; 1394 offset := f.offset; 1395 iounit := cp.msize-IOHDRSZ; 1396 if(count > iounit) 1397 count = iounit; 1398 1399 # Pick up where we left off last time if nothing has changed, 1400 # otherwise must scan from the beginning. 1401 1402 addr, slot: int; 1403 start: big; 1404 1405 if(offset == file.doffset){ # && file.qid.vers == file.dvers 1406 addr = file.dslot/DIRPERBUF; 1407 slot = file.dslot%DIRPERBUF; 1408 start = offset; 1409 } 1410 else{ 1411 addr = 0; 1412 slot = 0; 1413 start = big 0; 1414 } 1415 1416 nread := 0; 1417Dread: 1418 for(;;){ 1419 if(d.iob == nil){ 1420 # 1421 # This is just a check to ensure the entry hasn't 1422 # gone away during the read of each directory block. 1423 # 1424 e: string; 1425 (d, e) = Dentry.getd(file, Bread); 1426 if(d == nil) 1427 return ferr(f, e, file, nil); 1428 } 1429 p1 = d.getblk1(addr, 0); 1430 if(p1 == nil) 1431 break; 1432 if(p1.checktag(Tdir, QPNONE)) 1433 return ferr(f, Ephase, file, p1); 1434 1435 for(; slot < DIRPERBUF; slot++){ 1436 d1 = Dentry.get(p1, slot); 1437 if(!(d1.mode & DALLOC)) 1438 continue; 1439 dir := dir9p2(d1); 1440 n := styx->packdirsize(dir); 1441 if(n > count-nread){ 1442 p1.put(); 1443 break Dread; 1444 } 1445 data[nread:] = styx->packdir(dir); 1446 start += big n; 1447 if(start < offset) 1448 continue; 1449 if(count < n){ 1450 p1.put(); 1451 break Dread; 1452 } 1453 count -= n; 1454 nread += n; 1455 offset += big n; 1456 } 1457 p1.put(); 1458 slot = 0; 1459 addr++; 1460 } 1461 1462 file.doffset = offset; 1463 file.dvers = file.qid.vers; 1464 file.dslot = slot+DIRPERBUF*addr; 1465 1466 d.put(); 1467 file.unlock(); 1468 return ref Rmsg.Read(f.tag, data[0:nread]); 1469} 1470 1471rread(cp: ref Chan, f: ref Tmsg.Read): ref Rmsg 1472{ 1473 if((file := cp.getfid(f.fid, 0)) == nil) 1474 return err(f, Efid); 1475 if(!(file.open & FREAD)) 1476 return ferr(f, Eopen, file, nil); 1477 count := f.count; 1478 iounit := cp.msize-IOHDRSZ; 1479 if(count < 0 || count > iounit) 1480 return ferr(f, Ecount, file, nil); 1481 offset := f.offset; 1482 if(offset < big 0) 1483 return ferr(f, Eoffset, file, nil); 1484 1485 (d, e) := Dentry.getd(file, Bread); 1486 if(d == nil) 1487 return ferr(f, e, file, nil); 1488 if((t := file.tlock) != nil){ 1489 tim := now(); 1490 if(t.time < tim || t.file != file){ 1491 d.put(); 1492 return ferr(f, Ebroken, file, nil); 1493 } 1494 # renew the lock 1495 t.time = tim + TLOCK; 1496 } 1497 d.access(FREAD, file.uid); 1498 if(d.mode & DDIR) 1499 return dirread(cp, f, file, d); 1500 1501 if(offset+big count > d.size) 1502 count = int (d.size - offset); 1503 if(count < 0) 1504 count = 0; 1505 data := array[count] of byte; 1506 nread := 0; 1507 while(count > 0){ 1508 if(d.iob == nil){ 1509 # must check and reacquire entry 1510 (d, e) = Dentry.getd(file, Bread); 1511 if(d == nil) 1512 return ferr(f, e, file, nil); 1513 } 1514 addr := int (offset / big BUFSIZE); 1515 if(addr == file.lastra+1) 1516 ; # dbufread(p, d, addr+1); 1517 file.lastra = addr; 1518 o := int (offset % big BUFSIZE); 1519 n := BUFSIZE - o; 1520 if(n > count) 1521 n = count; 1522 p1 := d.getblk1(addr, 0); 1523 if(p1 != nil){ 1524 if(p1.checktag(Tfile, QPNONE)){ 1525 p1.put(); 1526 return ferr(f, Ephase, file, nil); 1527 } 1528 data[nread:] = p1.iobuf[o:o+n]; 1529 p1.put(); 1530 }else 1531 data[nread:] = emptyblock[0:n]; 1532 count -= n; 1533 nread += n; 1534 offset += big n; 1535 } 1536 d.put(); 1537 file.unlock(); 1538 return ref Rmsg.Read(f.tag, data[0:nread]); 1539} 1540 1541rwrite(cp: ref Chan, f: ref Tmsg.Write): ref Rmsg 1542{ 1543 if((file := cp.getfid(f.fid, 0)) == nil) 1544 return err(f, Efid); 1545 if(!(file.open & FWRITE)) 1546 return ferr(f, Eopen, file, nil); 1547 if(isro(file.fs) || (writegroup && !ingroup(file.uid, writegroup))) 1548 return ferr(f, Eronly, file, nil); 1549 count := len f.data; 1550 if(count < 0 || count > cp.msize-IOHDRSZ) 1551 return ferr(f, Ecount, file, nil); 1552 offset := f.offset; 1553 if(offset < big 0) 1554 return ferr(f, Eoffset, file, nil); 1555 1556 (d, e) := Dentry.getd(file, Bread|Bmod); 1557 if(d == nil) 1558 return ferr(f, e, file, nil); 1559 if((t := file.tlock) != nil){ 1560 tim := now(); 1561 if(t.time < tim || t.file != file){ 1562 d.put(); 1563 return ferr(f, Ebroken, file, nil); 1564 } 1565 # renew the lock 1566 t.time = tim + TLOCK; 1567 } 1568 d.access(FWRITE, file.uid); 1569 if(d.mode & DAPND) 1570 offset = d.size; 1571 end := offset + big count; 1572 if(end > d.size){ 1573 if(end > MAXFILESIZE) 1574 return ferr(f, Etoobig, file, nil); 1575 d.size = end; 1576 d.change(Usize); 1577 } 1578 d.update(); 1579 1580 nwrite := 0; 1581 while(count > 0){ 1582 if(d.iob == nil){ 1583 # must check and reacquire entry 1584 (d, e) = Dentry.getd(file, Bread|Bmod); 1585 if(d == nil) 1586 return ferr(f, e, file, nil); 1587 } 1588 addr := int (offset / big BUFSIZE); 1589 o := int (offset % big BUFSIZE); 1590 n := BUFSIZE - o; 1591 if(n > count) 1592 n = count; 1593 qpath := int d.qid.path; 1594 p1 := d.getblk1(addr, Tfile); 1595 if(p1 == nil) 1596 return ferr(f, Efull, file, nil); 1597 if(p1.checktag(Tfile, qpath)){ 1598 p1.put(); 1599 return ferr(f, Ealloc, file, nil); 1600 } 1601 p1.iobuf[o:] = f.data[nwrite:nwrite+n]; 1602 p1.flags |= Bmod; 1603 p1.put(); 1604 count -= n; 1605 nwrite += n; 1606 offset += big n; 1607 } 1608 d.put(); 1609 file.unlock(); 1610 return ref Rmsg.Write(f.tag, nwrite); 1611} 1612 1613doremove(f: ref File, iscon: int): string 1614{ 1615 if(isro(f.fs) || f.cons == 0 && (writegroup && !ingroup(f.uid, writegroup))) 1616 return Eronly; 1617 # 1618 # check permission on parent directory of file to be deleted 1619 # 1620 if(f.wpath == nil || f.wpath.addr == f.addr) 1621 return Ephase; 1622 (d1, e1) := Dentry.geta(f.fs, f.wpath.addr, f.wpath.slot, QPNONE, Bread); 1623 if(e1 != nil) 1624 return e1; 1625 if(!iscon && f.access(d1, DWRITE)){ 1626 d1.put(); 1627 return Eaccess; 1628 } 1629 d1.access(FWRITE, f.uid); 1630 d1.put(); 1631 1632 # 1633 # check on file to be deleted 1634 # 1635 (d, e) := Dentry.getd(f, Bread); 1636 if(e != nil) 1637 return e; 1638 1639 # 1640 # if deleting a directory, make sure it is empty 1641 # 1642 if(d.mode & DDIR) 1643 for(addr:=0; (p1 := d.getblk(addr, 0)) != nil; addr++){ 1644 if(p1.checktag(Tdir, int d.qid.path)){ 1645 p1.put(); 1646 d.put(); 1647 return Ephase; 1648 } 1649 for(slot:=0; slot<DIRPERBUF; slot++){ 1650 d1 = Dentry.get(p1, slot); 1651 if(!(d1.mode & DALLOC)) 1652 continue; 1653 p1.put(); 1654 d.put(); 1655 return Eempty; 1656 } 1657 p1.put(); 1658 } 1659 1660 # 1661 # do it 1662 # 1663 d.trunc(f.uid); 1664 d.buf[0:] = emptyblock[0:Dentrysize]; 1665 d.put(); 1666 return nil; 1667} 1668 1669clunk(cp: ref Chan, file: ref File, remove: int, wok: int): string 1670{ 1671 if((t := file.tlock) != nil){ 1672 if(t.file == file) 1673 t.time = 0; # free the lock 1674 file.tlock = nil; 1675 } 1676 if(remove) 1677 error := doremove(file, wok); 1678 file.open = 0; 1679 file.wpath = nil; 1680 cp.putfid(file); 1681 1682 return error; 1683} 1684 1685rclunk(cp: ref Chan, t: ref Tmsg.Clunk): ref Rmsg 1686{ 1687 if((file := cp.getfid(t.fid, 0)) == nil) 1688 return err(t, Efid); 1689 clunk(cp, file, file.open & FREMOV, 0); 1690 return ref Rmsg.Clunk(t.tag); 1691} 1692 1693rremove(cp: ref Chan, t: ref Tmsg.Remove): ref Rmsg 1694{ 1695 if((file := cp.getfid(t.fid, 0)) == nil) 1696 return err(t, Efid); 1697 e := clunk(cp, file, 1, cp == conschan); 1698 if(e != nil) 1699 return err(t, e); 1700 return ref Rmsg.Remove(t.tag); 1701} 1702 1703rstat(cp: ref Chan, f: ref Tmsg.Stat): ref Rmsg 1704{ 1705 if((file := cp.getfid(f.fid, 0)) == nil) 1706 return err(f, Efid); 1707 (d, e) := Dentry.getd(file, Bread); 1708 if(d == nil) 1709 return ferr(f, e, file, nil); 1710 dir := dir9p2(d); 1711 if(d.qid.path == big QPROOT) # stat of root gives time 1712 dir.atime = now(); 1713 d.put(); 1714 if(styx->packdirsize(dir) > cp.msize-IOHDRSZ) 1715 return ferr(f, Ersc, file, nil); 1716 file.unlock(); 1717 1718 return ref Rmsg.Stat(f.tag, dir); 1719} 1720 1721rwstat(cp: ref Chan, f: ref Tmsg.Wstat): ref Rmsg 1722{ 1723 if((file := cp.getfid(f.fid, 0)) == nil) 1724 return err(f, Efid); 1725 1726 # if user none, can't do anything unless in allow mode 1727 1728 if(file.uid == None && !wstatallow) 1729 return ferr(f, Eaccess, file, nil); 1730 1731 if(isro(file.fs) || (writegroup && !ingroup(file.uid, writegroup))) 1732 return ferr(f, Eronly, file, nil); 1733 1734 # 1735 # first get parent 1736 # 1737 p1: ref Iobuf; 1738 d1: ref Dentry; 1739 if(file.wpath != nil){ 1740 p1 = Iobuf.get(file.fs, file.wpath.addr, Bread); 1741 if(p1 == nil) 1742 return ferr(f, Ephase, file, p1); 1743 d1 = Dentry.get(p1, file.wpath.slot); 1744 if(d1 == nil || p1.checktag(Tdir, QPNONE) || !(d1.mode & DALLOC)) 1745 return ferr(f, Ephase, file, p1); 1746 } 1747 1748 # 1749 # now the file 1750 # 1751 (d, e) := Dentry.getd(file, Bread); 1752 if(d == nil) 1753 return ferr(f, e, file, p1); 1754 1755 # 1756 # Convert the message and fix up 1757 # fields not to be changed. 1758 # 1759 dir := f.stat; 1760 if(dir.uid == nil) 1761 uid := d.uid; 1762 else 1763 uid = strtouid(dir.uid); 1764 if(dir.gid == nil) 1765 gid := d.gid; 1766 else 1767 gid = strtouid(dir.gid); 1768 if(dir.name == nil) 1769 dir.name = d.name; 1770 else{ 1771 if((l := checkname9p2(dir.name)) == 0){ 1772 d.put(); 1773 return ferr(f, Ename, file, p1); 1774 } 1775 if(l+1 > NAMELEN){ 1776 d.put(); 1777 return ferr(f, Etoolong, file, p1); 1778 } 1779 } 1780 1781 # Before doing sanity checks, find out what the 1782 # new 'mode' should be: 1783 # if 'type' and 'mode' are both defaults, take the 1784 # new mode from the old directory entry; 1785 # else if 'type' is the default, use the new mode entry; 1786 # else if 'mode' is the default, create the new mode from 1787 # 'type' or'ed with the old directory mode; 1788 # else neither are defaults, use the new mode but check 1789 # it agrees with 'type'. 1790 1791 if(dir.qid.qtype == 16rFF && dir.mode == ~0){ 1792 dir.mode = d.mode & 8r777; 1793 if(d.mode & DLOCK) 1794 dir.mode |= DMEXCL; 1795 if(d.mode & DAPND) 1796 dir.mode |= DMAPPEND; 1797 if(d.mode & DDIR) 1798 dir.mode |= DMDIR; 1799 } 1800 else if(dir.qid.qtype == 16rFF){ 1801 # nothing to do 1802 } 1803 else if(dir.mode == ~0) 1804 dir.mode = (dir.qid.qtype<<24)|(d.mode & 8r777); 1805 else if(dir.qid.qtype != ((dir.mode>>24) & 16rFF)){ 1806 d.put(); 1807 return ferr(f, Eqidmode, file, p1); 1808 } 1809 1810 # Check for unknown type/mode bits 1811 # and an attempt to change the directory bit. 1812 1813 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|8r777)){ 1814 d.put(); 1815 return ferr(f, Enotm, file, p1); 1816 } 1817 if(d.mode & DDIR) 1818 mode := DMDIR; 1819 else 1820 mode = 0; 1821 if((dir.mode^mode) & DMDIR){ 1822 d.put(); 1823 return ferr(f, Enotd, file, p1); 1824 } 1825 1826 if(dir.mtime == ~0) 1827 dir.mtime = d.mtime; 1828 if(dir.length == ~big 0) 1829 dir.length = big d.size; 1830 1831 1832 # Currently, can't change length. 1833 1834 if(dir.length != big d.size){ 1835 d.put(); 1836 return ferr(f, Enotl, file, p1); 1837 } 1838 1839 1840 # if chown, 1841 # must be god 1842 # wstatallow set to allow chown during boot 1843 1844 if(uid != d.uid && !wstatallow){ 1845 d.put(); 1846 return ferr(f, Enotu, file, p1); 1847 } 1848 1849 # if chgroup, 1850 # must be either 1851 # a) owner and in new group 1852 # b) leader of both groups 1853 # wstatallow and writeallow are set to allow chgrp during boot 1854 1855 while(gid != d.gid){ 1856 if(wstatallow || writeallow) 1857 break; 1858 if(d.uid == file.uid && ingroup(file.uid, gid)) 1859 break; 1860 if(leadgroup(file.uid, gid)) 1861 if(leadgroup(file.uid, d.gid)) 1862 break; 1863 d.put(); 1864 return ferr(f, Enotg, file, p1); 1865 } 1866 1867 # if rename, 1868 # must have write permission in parent 1869 1870 while(d.name != dir.name){ 1871 1872 # drop entry to prevent deadlock, then 1873 # check that destination name is valid and unique 1874 1875 d.put(); 1876 if(checkname9p2(dir.name) == 0 || d1 == nil) 1877 return ferr(f, Ename, file, p1); 1878 if(dir.name == "." || dir.name == "..") 1879 return ferr(f, Edot, file, p1); 1880 1881 1882 for(addr := 0; ; addr++){ 1883 if((p := d1.getblk(addr, 0)) == nil) 1884 break; 1885 if(p.checktag(Tdir, int d1.qid.path)){ 1886 p.put(); 1887 continue; 1888 } 1889 for(slot := 0; slot < DIRPERBUF; slot++){ 1890 d = Dentry.get(p, slot); 1891 if(!(d.mode & DALLOC)) 1892 continue; 1893 if(dir.name == d.name){ 1894 p.put(); 1895 return ferr(f, Eexist, file, p1); 1896 } 1897 } 1898 p.put(); 1899 } 1900 1901 # reacquire entry 1902 1903 (d, nil) = Dentry.getd(file, Bread); 1904 if(d == nil) 1905 return ferr(f, Ephase, file, p1); 1906 1907 if(wstatallow || writeallow) # set to allow rename during boot 1908 break; 1909 if(d1 == nil || file.access(d1, DWRITE)){ 1910 d.put(); 1911 return ferr(f, Eaccess, file, p1); 1912 } 1913 break; 1914 } 1915 1916 # if mode/time, either 1917 # a) owner 1918 # b) leader of either group 1919 1920 mode = dir.mode & 8r777; 1921 if(dir.mode & DMAPPEND) 1922 mode |= DAPND; 1923 if(dir.mode & DMEXCL) 1924 mode |= DLOCK; 1925 while(d.mtime != dir.mtime || ((d.mode^mode) & (DAPND|DLOCK|8r777))){ 1926 if(wstatallow) # set to allow chmod during boot 1927 break; 1928 if(d.uid == file.uid) 1929 break; 1930 if(leadgroup(file.uid, gid)) 1931 break; 1932 if(leadgroup(file.uid, d.gid)) 1933 break; 1934 d.put(); 1935 return ferr(f, Enotu, file, p1); 1936 } 1937 d.mtime = dir.mtime; 1938 d.uid = uid; 1939 d.gid = gid; 1940 d.mode = (mode & (DAPND|DLOCK|8r777)) | (d.mode & (DALLOC|DDIR)); 1941 1942 d.name = dir.name; 1943 d.access(FWSTAT, file.uid); 1944 d.change(~0); 1945 d.put(); 1946 1947 if(p1 != nil) 1948 p1.put(); 1949 file.unlock(); 1950 1951 return ref Rmsg.Wstat(f.tag); 1952} 1953 1954superok(set: int): int 1955{ 1956 sb := Superb.get(thedevice, Bread|Bmod|Bimm); 1957 ok := sb.fsok; 1958 sb.fsok = set; 1959 if(debug) 1960 sb.print(); 1961 sb.touched(); 1962 sb.put(); 1963 return ok; 1964} 1965 1966# little-endian 1967get2(a: array of byte, o: int): int 1968{ 1969 return (int a[o+1]<<8) | int a[o]; 1970} 1971 1972get2s(a: array of byte, o: int): int 1973{ 1974 v := (int a[o+1]<<8) | int a[o]; 1975 if(v & 16r8000) 1976 v |= ~0 << 8; 1977 return v; 1978} 1979 1980get4(a: array of byte, o: int): int 1981{ 1982 return (int a[o+3]<<24) | (int a[o+2] << 16) | (int a[o+1]<<8) | int a[o]; 1983} 1984 1985put2(a: array of byte, o: int, v: int) 1986{ 1987 a[o] = byte v; 1988 a[o+1] = byte (v>>8); 1989} 1990 1991put4(a: array of byte, o: int, v: int) 1992{ 1993 a[o] = byte v; 1994 a[o+1] = byte (v>>8); 1995 a[o+2] = byte (v>>16); 1996 a[o+3] = byte (v>>24); 1997} 1998 1999Tag.unpack(a: array of byte): Tag 2000{ 2001 return Tag(get2(a,2), get4(a,4)); 2002} 2003 2004Tag.pack(t: self Tag, a: array of byte) 2005{ 2006 put2(a, 0, 0); 2007 put2(a, 2, t.tag); 2008 if(t.path != QPNONE) 2009 put4(a, 4, t.path & ~QPDIR); 2010} 2011 2012Superb.get(dev: ref Device, flags: int): ref Superb 2013{ 2014 p := Iobuf.get(dev, SUPERADDR, flags); 2015 if(p == nil) 2016 return nil; 2017 if(p.checktag(Tsuper, QPSUPER)){ 2018 p.put(); 2019 return nil; 2020 } 2021 sb := Superb.unpack(p.iobuf); 2022 sb.iob = p; 2023 return sb; 2024} 2025 2026Superb.touched(s: self ref Superb) 2027{ 2028 s.iob.flags |= Bmod; 2029} 2030 2031Superb.put(sb: self ref Superb) 2032{ 2033 if(sb.iob == nil) 2034 return; 2035 if(sb.iob.flags & Bmod) 2036 sb.pack(sb.iob.iobuf); 2037 sb.iob.put(); 2038 sb.iob = nil; 2039} 2040 2041# this is the disk structure 2042# Superb: 2043# Super1; 2044# Fbuf fbuf; 2045# Fbuf: 2046# nfree[4] 2047# free[] # based on BUFSIZE 2048# Super1: 2049# long fstart; 2050# long fsize; 2051# long tfree; 2052# long qidgen; # generator for unique ids 2053# long fsok; # file system ok 2054# long roraddr; # dump root addr 2055# long last; # last super block addr 2056# long next; # next super block addr 2057 2058Ofstart: con 0; 2059Ofsize: con Ofstart+4; 2060Otfree: con Ofsize+4; 2061Oqidgen: con Otfree+4; 2062Ofsok: con Oqidgen+4; 2063Ororaddr: con Ofsok+4; 2064Olast: con Ororaddr+4; 2065Onext: con Olast+4; 2066Super1size: con Onext+4; 2067 2068Superb.unpack(a: array of byte): ref Superb 2069{ 2070 s := ref Superb; 2071 s.fstart = get4(a, Ofstart); 2072 s.fsize = get4(a, Ofsize); 2073 s.tfree = get4(a, Otfree); 2074 s.qidgen = get4(a, Oqidgen); 2075 s.fsok = get4(a, Ofsok); 2076 s.fbuf = a[Super1size:]; 2077 return s; 2078} 2079 2080Superb.pack(s: self ref Superb, a: array of byte) 2081{ 2082 put4(a, Ofstart, s.fstart); 2083 put4(a, Ofsize, s.fsize); 2084 put4(a, Otfree, s.tfree); 2085 put4(a, Oqidgen, s.qidgen); 2086 put4(a, Ofsok, s.fsok); 2087} 2088 2089Superb.print(sb: self ref Superb) 2090{ 2091 sys->print("fstart=%ud fsize=%ud tfree=%ud qidgen=%ud fsok=%d\n", 2092 sb.fstart, sb.fsize, sb.tfree, sb.qidgen, sb.fsok); 2093} 2094 2095Dentry.get(p: ref Iobuf, slot: int): ref Dentry 2096{ 2097 if(p == nil) 2098 return nil; 2099 buf := p.iobuf[(slot%DIRPERBUF)*Dentrysize:]; 2100 d := Dentry.unpack(buf); 2101 d.iob = p; 2102 d.buf = buf; 2103 return d; 2104} 2105 2106Dentry.geta(fs: ref Device, addr: int, slot: int, qpath: int, mode: int): (ref Dentry, string) 2107{ 2108 p := Iobuf.get(fs, addr, mode); 2109 if(p == nil || p.checktag(Tdir, qpath)){ 2110 if(p != nil) 2111 p.put(); 2112 return (nil, Ealloc); 2113 } 2114 d := Dentry.get(p, slot); 2115 if(d == nil || !(d.mode & DALLOC)){ 2116 p.put(); 2117 return (nil, Ealloc); 2118 } 2119 return (d, nil); 2120} 2121 2122Dentry.getd(file: ref File, mode: int): (ref Dentry, string) 2123{ 2124 (d, e) := Dentry.geta(file.fs, file.addr, file.slot, QPNONE, mode); # QPNONE should be file.wpath's path 2125 if(e != nil) 2126 return (nil, e); 2127 if(file.qid.path != d.qid.path || (file.qid.qtype&QTDIR) != (d.qid.qtype&QTDIR)){ 2128 d.put(); 2129 return (nil, Eqid); 2130 } 2131 return (d, nil); 2132} 2133 2134# this is the disk structure: 2135# char name[NAMELEN]; 2136# short uid; 2137# short gid; [2*2] 2138# ushort mode; 2139# #define DALLOC 0x8000 2140# #define DDIR 0x4000 2141# #define DAPND 0x2000 2142# #define DLOCK 0x1000 2143# #define DREAD 0x4 2144# #define DWRITE 0x2 2145# #define DEXEC 0x1 2146# [ushort muid] [2*2] 2147# Qid.path; [4] 2148# Qid.version; [4] 2149# long size; [4] 2150# long dblock[NDBLOCK]; 2151# long iblock; 2152# long diblock; 2153# long atime; 2154# long mtime; 2155 2156Oname: con 0; 2157Ouid: con Oname+NAMELEN; 2158Ogid: con Ouid+2; 2159Omode: con Ogid+2; 2160Omuid: con Omode+2; 2161Opath: con Omuid+2; 2162Overs: con Opath+4; 2163Osize: con Overs+4; 2164Odblock: con Osize+4; 2165Oiblock: con Odblock+NDBLOCK*4; 2166Odiblock: con Oiblock+4; 2167Oatime: con Odiblock+4; 2168Omtime: con Oatime+4; 2169Dentrysize: con Omtime+4; 2170 2171Dentry.unpack(a: array of byte): ref Dentry 2172{ 2173 d := ref Dentry; 2174 for(i:=0; i<NAMELEN; i++) 2175 if(int a[i] == 0) 2176 break; 2177 d.name = string a[0:i]; 2178 d.uid = get2s(a, Ouid); 2179 d.gid = get2s(a, Ogid); 2180 d.mode = get2(a, Omode); 2181 d.muid = get2(a, Omuid); # note: not set by Plan 9's kfs 2182 d.qid = mkqid(get4(a, Opath), get4(a, Overs), d.mode); 2183 d.size = big get4(a, Osize) & big 16rFFFFFFFF; 2184 d.atime = get4(a, Oatime); 2185 d.mtime = get4(a, Omtime); 2186 d.mod = 0; 2187 return d; 2188} 2189 2190Dentry.change(d: self ref Dentry, f: int) 2191{ 2192 d.mod |= f; 2193} 2194 2195Dentry.update(d: self ref Dentry) 2196{ 2197 f := d.mod; 2198 d.mod = 0; 2199 if(d.iob == nil || (d.iob.flags & Bmod) == 0){ 2200 if(f != 0) 2201 panic("Dentry.update"); 2202 return; 2203 } 2204 a := d.buf; 2205 if(f & Uname){ 2206 b := array of byte d.name; 2207 for(i := 0; i < NAMELEN; i++) 2208 if(i < len b) 2209 a[i] = b[i]; 2210 else 2211 a[i] = byte 0; 2212 } 2213 if(f & Uids){ 2214 put2(a, Ouid, d.uid); 2215 put2(a, Ogid, d.gid); 2216 } 2217 if(f & Umode) 2218 put2(a, Omode, d.mode); 2219 if(f & Uqid){ 2220 path := int d.qid.path; 2221 if(d.mode & DDIR) 2222 path |= QPDIR; 2223 put4(a, Opath, path); 2224 put4(a, Overs, d.qid.vers); 2225 } 2226 if(f & Usize) 2227 put4(a, Osize, int d.size); 2228 if(f & Utime){ 2229 put4(a, Omtime, d.mtime); 2230 put4(a, Oatime, d.atime); 2231 } 2232 d.iob.flags |= Bmod; 2233} 2234 2235Dentry.access(d: self ref Dentry, f: int, uid: int) 2236{ 2237 if((p := d.iob) != nil && !readonly){ 2238 if((f & (FWRITE|FWSTAT)) == 0 && noatime) 2239 return; 2240 if(f & (FREAD|FWRITE|FWSTAT)){ 2241 d.atime = now(); 2242 put4(d.buf, Oatime, d.atime); 2243 p.flags |= Bmod; 2244 } 2245 if(f & FWRITE){ 2246 d.mtime = now(); 2247 put4(d.buf, Omtime, d.mtime); 2248 d.muid = uid; 2249 put2(d.buf, Omuid, uid); 2250 d.qid.vers++; 2251 put4(d.buf, Overs, d.qid.vers); 2252 p.flags |= Bmod; 2253 } 2254 } 2255} 2256 2257# 2258# release the directory entry buffer and thus the 2259# lock on both buffer and entry, typically during i/o, 2260# to be reacquired later if needed 2261# 2262Dentry.release(d: self ref Dentry) 2263{ 2264 if(d.iob != nil){ 2265 d.update(); 2266 d.iob.put(); 2267 d.iob = nil; 2268 d.buf = nil; 2269 } 2270} 2271 2272Dentry.getblk(d: self ref Dentry, a: int, tag: int): ref Iobuf 2273{ 2274 addr := d.rel2abs(a, tag, 0); 2275 if(addr == 0) 2276 return nil; 2277 return Iobuf.get(thedevice, addr, Bread); 2278} 2279 2280# 2281# same as Dentry.buf but calls d.release 2282# to reduce interference. 2283# 2284Dentry.getblk1(d: self ref Dentry, a: int, tag: int): ref Iobuf 2285{ 2286 addr := d.rel2abs(a, tag, 1); 2287 if(addr == 0) 2288 return nil; 2289 return Iobuf.get(thedevice, addr, Bread); 2290} 2291 2292Dentry.rel2abs(d: self ref Dentry, a: int, tag: int, putb: int): int 2293{ 2294 if(a < 0){ 2295 sys->print("Dentry.rel2abs: neg\n"); 2296 return 0; 2297 } 2298 p := d.iob; 2299 if(p == nil || d.buf == nil) 2300 panic("nil iob"); 2301 data := d.buf; 2302 qpath := int d.qid.path; 2303 dev := p.dev; 2304 if(a < NDBLOCK){ 2305 addr := get4(data, Odblock+a*4); 2306 if(addr == 0 && tag){ 2307 addr = balloc(dev, tag, qpath); 2308 put4(data, Odblock+a*4, addr); 2309 p.flags |= Bmod|Bimm; 2310 } 2311 if(putb) 2312 d.release(); 2313 return addr; 2314 } 2315 a -= NDBLOCK; 2316 if(a < INDPERBUF){ 2317 addr := get4(data, Oiblock); 2318 if(addr == 0 && tag){ 2319 addr = balloc(dev, Tind1, qpath); 2320 put4(data, Oiblock, addr); 2321 p.flags |= Bmod|Bimm; 2322 } 2323 if(putb) 2324 d.release(); 2325 return indfetch(dev, qpath, addr, a, Tind1, tag); 2326 } 2327 a -= INDPERBUF; 2328 if(a < INDPERBUF2){ 2329 addr := get4(data, Odiblock); 2330 if(addr == 0 && tag){ 2331 addr = balloc(dev, Tind2, qpath); 2332 put4(data, Odiblock, addr); 2333 p.flags |= Bmod|Bimm; 2334 } 2335 if(putb) 2336 d.release(); 2337 addr = indfetch(dev, qpath, addr, a/INDPERBUF, Tind2, Tind1); 2338 return indfetch(dev, qpath, addr, a%INDPERBUF, Tind1, tag); 2339 } 2340 if(putb) 2341 d.release(); 2342 sys->print("Dentry.buf: trip indirect\n"); 2343 return 0; 2344} 2345 2346indfetch(dev: ref Device, path: int, addr: int, a: int, itag: int, tag: int): int 2347{ 2348 if(addr == 0) 2349 return 0; 2350 bp := Iobuf.get(dev, addr, Bread); 2351 if(bp == nil){ 2352 sys->print("ind fetch bp = nil\n"); 2353 return 0; 2354 } 2355 if(bp.checktag(itag, path)){ 2356 sys->print("ind fetch tag\n"); 2357 bp.put(); 2358 return 0; 2359 } 2360 addr = get4(bp.iobuf, a*4); 2361 if(addr == 0 && tag){ 2362 addr = balloc(dev, tag, path); 2363 if(addr != 0){ 2364 put4(bp.iobuf, a*4, addr); 2365 bp.flags |= Bmod; 2366 if(localfs || tag == Tdir) 2367 bp.flags |= Bimm; 2368 bp.settag(itag, path); 2369 } 2370 } 2371 bp.put(); 2372 return addr; 2373} 2374 2375balloc(dev: ref Device, tag: int, qpath: int): int 2376{ 2377 # TO DO: cache superblock to reduce pack/unpack 2378 sb := Superb.get(dev, Bread|Bmod); 2379 if(sb == nil) 2380 panic("balloc: super block"); 2381 n := get4(sb.fbuf, 0); 2382 n--; 2383 sb.tfree--; 2384 if(n < 0 || n >= FEPERBUF) 2385 panic("balloc: bad freelist"); 2386 a := get4(sb.fbuf, 4+n*4); 2387 if(n == 0){ 2388 if(a == 0){ 2389 sb.tfree = 0; 2390 sb.touched(); 2391 sb.put(); 2392 return 0; 2393 } 2394 bp := Iobuf.get(dev, a, Bread); 2395 if(bp == nil || bp.checktag(Tfree, QPNONE)){ 2396 if(bp != nil) 2397 bp.put(); 2398 sb.put(); 2399 return 0; 2400 } 2401 sb.fbuf[0:] = bp.iobuf[0:(FEPERBUF+1)*4]; 2402 sb.touched(); 2403 bp.put(); 2404 }else{ 2405 put4(sb.fbuf, 0, n); 2406 sb.touched(); 2407 } 2408 bp := Iobuf.get(dev, a, Bmod); 2409 bp.iobuf[0:] = emptyblock; 2410 bp.settag(tag, qpath); 2411 if(tag == Tind1 || tag == Tind2 || tag == Tdir) 2412 bp.flags |= Bimm; 2413 bp.put(); 2414 sb.put(); 2415 return a; 2416} 2417 2418bfree(dev: ref Device, addr: int, d: int) 2419{ 2420 if(addr == 0) 2421 return; 2422 if(d > 0){ 2423 d--; 2424 p := Iobuf.get(dev, addr, Bread); 2425 if(p != nil){ 2426 for(i:=INDPERBUF-1; i>=0; i--){ 2427 a := get4(p.iobuf, i*4); 2428 bfree(dev, a, d); 2429 } 2430 p.put(); 2431 } 2432 } 2433 2434 # stop outstanding i/o 2435 p := Iobuf.get(dev, addr, Bprobe); 2436 if(p != nil){ 2437 p.flags &= ~(Bmod|Bimm); 2438 p.put(); 2439 } 2440 2441 s := Superb.get(dev, Bread|Bmod); 2442 if(s == nil) 2443 panic("bfree: super block"); 2444 addfree(dev, addr, s); 2445 s.put(); 2446} 2447 2448addfree(dev: ref Device, addr: int, sb: ref Superb) 2449{ 2450 if(addr >= sb.fsize){ 2451 sys->print("addfree: bad addr %ud\n", addr); 2452 return; 2453 } 2454 n := get4(sb.fbuf, 0); 2455 if(n < 0 || n > FEPERBUF) 2456 panic("addfree: bad freelist"); 2457 if(n >= FEPERBUF){ 2458 p := Iobuf.get(dev, addr, Bmod); 2459 if(p == nil) 2460 panic("addfree: Iobuf.get"); 2461 p.iobuf[0:] = sb.fbuf[0:(1+FEPERBUF)*4]; 2462 sb.fbuf[0:] = emptyblock[0:(1+FEPERBUF)*4]; # clear it for debugging 2463 p.settag(Tfree, QPNONE); 2464 p.put(); 2465 n = 0; 2466 } 2467 put4(sb.fbuf, 4+n*4, addr); 2468 put4(sb.fbuf, 0, n+1); 2469 sb.tfree++; 2470 if(addr >= sb.fsize) 2471 sb.fsize = addr+1; 2472 sb.touched(); 2473} 2474 2475qidpathgen(dev: ref Device): int 2476{ 2477 sb := Superb.get(dev, Bread|Bmod); 2478 if(sb == nil) 2479 panic("qidpathgen: super block"); 2480 sb.qidgen++; 2481 path := sb.qidgen; 2482 sb.touched(); 2483 sb.put(); 2484 return path; 2485} 2486 2487Dentry.trunc(d: self ref Dentry, uid: int) 2488{ 2489 p := d.iob; 2490 data := d.buf; 2491 bfree(p.dev, get4(data, Odiblock), 2); 2492 put4(data, Odiblock, 0); 2493 bfree(p.dev, get4(data, Oiblock), 1); 2494 put4(data, Oiblock, 0); 2495 for(i:=NDBLOCK-1; i>=0; i--){ 2496 bfree(p.dev, get4(data, Odblock+i*4), 0); 2497 put4(data, Odblock+i*4, 0); 2498 } 2499 d.size = big 0; 2500 d.change(Usize); 2501 p.flags |= Bmod|Bimm; 2502 d.access(FWRITE, uid); 2503 d.update(); 2504} 2505 2506Dentry.put(d: self ref Dentry) 2507{ 2508 p := d.iob; 2509 if(p == nil || d.buf == nil) 2510 return; 2511 d.update(); 2512 p.put(); 2513 d.iob = nil; 2514 d.buf = nil; 2515} 2516 2517Dentry.print(d: self ref Dentry) 2518{ 2519 sys->print("name=%#q uid=%d gid=%d mode=#%8.8ux qid.path=#%bux qid.vers=%ud size=%bud\n", 2520 d.name, d.uid, d.gid, d.mode, d.qid.path, d.qid.vers, d.size); 2521 p := d.iob; 2522 if(p != nil && (data := p.iobuf) != nil){ 2523 sys->print("\tdblock="); 2524 for(i := 0; i < NDBLOCK; i++) 2525 sys->print(" %d", get4(data, Odblock+i*4)); 2526 sys->print(" iblock=%ud diblock=%ud\n", get4(data, Oiblock), get4(data, Odiblock)); 2527 } 2528} 2529 2530HWidth: con 5; # buffers per line 2531 2532hiob: array of ref Hiob; 2533 2534iobufinit(niob: int) 2535{ 2536 nhiob := niob/HWidth; 2537 while(!prime(nhiob)) 2538 nhiob++; 2539 hiob = array[nhiob] of {* => ref Hiob(nil, Lock.new(), 0)}; 2540 # allocate the buffers now 2541 for(i := 0; i < len hiob; i++){ 2542 h := hiob[i]; 2543 while(h.niob < HWidth) 2544 h.newbuf(); 2545 } 2546} 2547 2548iobufclear() 2549{ 2550 # eliminate the cyclic references 2551 for(i := 0; i < len hiob; i++){ 2552 h := hiob[i]; 2553 while(--h.niob >= 0){ 2554 p := hiob[i].link; 2555 hiob[i].link = p.fore; 2556 p.fore = p.back = nil; 2557 p = nil; 2558 } 2559 } 2560} 2561 2562prime(n: int): int 2563{ 2564 if((n%2) == 0) 2565 return 0; 2566 for(i:=3;; i+=2) { 2567 if((n%i) == 0) 2568 return 0; 2569 if(i*i >= n) 2570 return 1; 2571 } 2572} 2573 2574Hiob.newbuf(hb: self ref Hiob): ref Iobuf 2575{ 2576 # hb must be locked 2577 p := ref Iobuf; 2578 p.qlock = chan[1] of int; 2579 q := hb.link; 2580 if(q != nil){ 2581 p.fore = q; 2582 p.back = q.back; 2583 q.back = p; 2584 p.back.fore = p; 2585 }else{ 2586 hb.link = p; 2587 p.fore = p; 2588 p.back = p; 2589 } 2590 p.dev = devnone; 2591 p.addr = -1; 2592 p.flags = 0; 2593 p.xiobuf = array[RBUFSIZE] of byte; 2594 hb.niob++; 2595 return p; 2596} 2597 2598Iobuf.get(dev: ref Device, addr: int, flags: int): ref Iobuf 2599{ 2600 hb := hiob[addr%len hiob]; 2601 p: ref Iobuf; 2602Search: 2603 for(;;){ 2604 hb.lk.lock(); 2605 s := hb.link; 2606 2607 # see if it's active 2608 p = s; 2609 do{ 2610 if(p.addr == addr && p.dev == dev){ 2611 if(p != s){ 2612 p.back.fore = p.fore; 2613 p.fore.back = p.back; 2614 p.fore = s; 2615 p.back = s.back; 2616 s.back = p; 2617 p.back.fore = p; 2618 hb.link = p; 2619 } 2620 hb.lk.unlock(); 2621 p.lock(); 2622 if(p.addr != addr || p.dev != dev){ 2623 # lost race 2624 p.unlock(); 2625 continue Search; 2626 } 2627 p.flags |= flags; 2628 p.iobuf = p.xiobuf; 2629 return p; 2630 } 2631 }while((p = p.fore) != s); 2632 if(flags == Bprobe){ 2633 hb.lk.unlock(); 2634 return nil; 2635 } 2636 2637 # steal the oldest unlocked buffer 2638 do{ 2639 p = s.back; 2640 if(p.canlock()){ 2641 # TO DO: if Bmod, write it out and restart Hashed 2642 # for now we needn't because Iobuf.put is synchronous 2643 if(p.flags & Bmod) 2644 sys->print("Bmod unexpected (%ud)\n", p.addr); 2645 hb.link = p; 2646 p.dev = dev; 2647 p.addr = addr; 2648 p.flags = flags; 2649 break Search; 2650 } 2651 s = p; 2652 }while(p != hb.link); 2653 2654 # no unlocked blocks available; add a new one 2655 p = hb.newbuf(); 2656 p.lock(); # return it locked 2657 break; 2658 } 2659 2660 p.dev = dev; 2661 p.addr = addr; 2662 p.flags = flags; 2663 hb.lk.unlock(); 2664 p.iobuf = p.xiobuf; 2665 if(flags & Bread){ 2666 if(wrenread(dev.fd, addr, p.iobuf)){ 2667 eprint(sys->sprint("error reading block %ud: %r", addr)); 2668 p.flags = 0; 2669 p.dev = devnone; 2670 p.addr = -1; 2671 p.iobuf = nil; 2672 p.unlock(); 2673 return nil; 2674 } 2675 } 2676 return p; 2677} 2678 2679Iobuf.put(p: self ref Iobuf) 2680{ 2681 if(p.flags & Bmod) 2682 p.flags |= Bimm; # temporary; see comment in Iobuf.get 2683 if(p.flags & Bimm){ 2684 if(!(p.flags & Bmod)) 2685 eprint(sys->sprint("imm and no mod (%d)", p.addr)); 2686 if(!wrenwrite(p.dev.fd, p.addr, p.iobuf)) 2687 p.flags &= ~(Bmod|Bimm); 2688 else 2689 panic(sys->sprint("error writing block %ud: %r", p.addr)); 2690 } 2691 p.iobuf = nil; 2692 p.unlock(); 2693} 2694 2695Iobuf.lock(p: self ref Iobuf) 2696{ 2697 p.qlock <-= 1; 2698} 2699 2700Iobuf.canlock(p: self ref Iobuf): int 2701{ 2702 alt{ 2703 p.qlock <-= 1 => 2704 return 1; 2705 * => 2706 return 0; 2707 } 2708} 2709 2710Iobuf.unlock(p: self ref Iobuf) 2711{ 2712 <-p.qlock; 2713} 2714 2715File.access(f: self ref File, d: ref Dentry, m: int): int 2716{ 2717 if(wstatallow) 2718 return 0; 2719 2720 # none gets only other permissions 2721 2722 if(f.uid != None){ 2723 if(f.uid == d.uid) # owner 2724 if((m<<6) & d.mode) 2725 return 0; 2726 if(ingroup(f.uid, d.gid)) # group membership 2727 if((m<<3) & d.mode) 2728 return 0; 2729 } 2730 2731 # 2732 # other access for everyone except members of group "noworld" 2733 # 2734 if(m & d.mode){ 2735 # 2736 # walk directories regardless. 2737 # otherwise it's impossible to get 2738 # from the root to noworld's directories. 2739 # 2740 if((d.mode & DDIR) && (m == DEXEC)) 2741 return 0; 2742 if(!ingroup(f.uid, Noworld)) 2743 return 0; 2744 } 2745 return 1; 2746} 2747 2748tagname(t: int): string 2749{ 2750 case t { 2751 Tnone => return "Tnone"; 2752 Tsuper => return "Tsuper"; 2753 Tdir => return "Tdir"; 2754 Tind1 => return "Tind1"; 2755 Tind2 => return "Tind2"; 2756 Tfile => return "Tfile"; 2757 Tfree => return "Tfree"; 2758 Tbuck => return "Tbuck"; 2759 Tvirgo => return "Tvirgo"; 2760 Tcache => return "Tcache"; 2761 * => return sys->sprint("%d", t); 2762 } 2763} 2764 2765Iobuf.checktag(p: self ref Iobuf, tag: int, qpath: int): int 2766{ 2767 t := Tag.unpack(p.iobuf[BUFSIZE:]); 2768 if(t.tag != tag){ 2769 if(1) 2770 eprint(sys->sprint(" tag = %s; expected %s; addr = %ud\n", 2771 tagname(t.tag), tagname(tag), p.addr)); 2772 return 2; 2773 } 2774 if(qpath != QPNONE){ 2775 qpath &= ~QPDIR; 2776 if(qpath != t.path){ 2777 if(qpath == (t.path&~QPDIR)) # old bug 2778 return 0; 2779 if(1) 2780 eprint(sys->sprint(" tag/path = %ux; expected %s/%ux\n", 2781 t.path, tagname(tag), qpath)); 2782 return 1; 2783 } 2784 } 2785 return 0; 2786} 2787 2788Iobuf.settag(p: self ref Iobuf, tag: int, qpath: int) 2789{ 2790 Tag(tag, qpath).pack(p.iobuf[BUFSIZE:]); 2791 p.flags |= Bmod; 2792} 2793 2794badmagic := 0; 2795wmagic := "kfs wren device\n"; 2796 2797wrenream(dev: ref Device) 2798{ 2799 if(RBUFSIZE % 512) 2800 panic(sys->sprint("kfs: bad buffersize(%d): restart a multiple of 512", RBUFSIZE)); 2801 if(RBUFSIZE > MAXBUFSIZE) 2802 panic(sys->sprint("kfs: bad buffersize(%d): must be at most %d", RBUFSIZE, MAXBUFSIZE)); 2803 sys->print("kfs: reaming the file system using %d byte blocks\n", RBUFSIZE); 2804 buf := array[RBUFSIZE] of {* => byte 0}; 2805 buf[256:] = sys->aprint("%s%d\n", wmagic, RBUFSIZE); 2806 if(sys->seek(dev.fd, big 0, 0) < big 0 || sys->write(dev.fd, buf, len buf) != len buf) 2807 panic("can't ream disk"); 2808} 2809 2810wreninit(dev: ref Device): int 2811{ 2812 (ok, nil) := sys->fstat(dev.fd); 2813 if(ok < 0) 2814 return 0; 2815 buf := array[MAXBUFSIZE] of byte; 2816 sys->seek(dev.fd, big 0, 0); 2817 n := sys->read(dev.fd, buf, len buf); 2818 if(n < len buf) 2819 return 0; 2820 badmagic = 0; 2821 RBUFSIZE = 1024; 2822 if(string buf[256:256+len wmagic] != wmagic){ 2823 badmagic = 1; 2824 return 0; 2825 } 2826 RBUFSIZE = int string buf[256+len wmagic:256+len wmagic+12]; 2827 if(RBUFSIZE % 512) 2828 error("bad block size"); 2829 return 1; 2830} 2831 2832wrenread(fd: ref Sys->FD, addr: int, a: array of byte): int 2833{ 2834 return sys->pread(fd, a, len a, big addr * big RBUFSIZE) != len a; 2835} 2836 2837wrenwrite(fd: ref Sys->FD, addr: int, a: array of byte): int 2838{ 2839 return sys->pwrite(fd, a, len a, big addr * big RBUFSIZE) != len a; 2840} 2841 2842wrentag(buf: array of byte, tag: int, qpath: int): int 2843{ 2844 t := Tag.unpack(buf[BUFSIZE:]); 2845 return t.tag != tag || (qpath&~QPDIR) != t.path; 2846} 2847 2848wrencheck(fd: ref Sys->FD): int 2849{ 2850 if(badmagic) 2851 return 1; 2852 buf := array[RBUFSIZE] of byte; 2853 if(wrenread(fd, SUPERADDR, buf) || wrentag(buf, Tsuper, QPSUPER) || 2854 wrenread(fd, ROOTADDR, buf) || wrentag(buf, Tdir, QPROOT)) 2855 return 1; 2856 d0 := Dentry.unpack(buf); 2857 if(d0.mode & DALLOC) 2858 return 0; 2859 return 1; 2860} 2861 2862wrensize(dev: ref Device): int 2863{ 2864 (ok, d) := sys->fstat(dev.fd); 2865 if(ok < 0) 2866 return -1; 2867 return int (d.length / big RBUFSIZE); 2868} 2869 2870checkname9p2(s: string): int 2871{ 2872 for(i := 0; i < len s; i++) 2873 if(s[i] <= 8r40) 2874 return 0; 2875 return styx->utflen(s); 2876} 2877 2878isro(d: ref Device): int 2879{ 2880 return d == nil || d.ronly; 2881} 2882 2883tlocks: list of ref Tlock; 2884 2885tlocked(f: ref File, d: ref Dentry): ref Tlock 2886{ 2887 tim := now(); 2888 path := int d.qid.path; 2889 t1: ref Tlock; 2890 for(l := tlocks; l != nil; l = tl l){ 2891 t := hd l; 2892 if(t.qpath == path && t.time >= tim && t.dev == f.fs) 2893 return nil; # it's locked 2894 if(t.file == nil || t1 == nil && t.time < tim) 2895 t1 = t; 2896 } 2897 t := t1; 2898 if(t == nil) 2899 t = ref Tlock; 2900 t.dev = f.fs; 2901 t.qpath = path; 2902 t.time = tim + TLOCK; 2903 tlocks = t :: tlocks; 2904 return t; 2905} 2906 2907mkqid(path: int, vers: int, mode: int): Qid 2908{ 2909 qid: Qid; 2910 2911 qid.path = big (path & ~QPDIR); 2912 qid.vers = vers; 2913 qid.qtype = 0; 2914 if(mode & DDIR) 2915 qid.qtype |= QTDIR; 2916 if(mode & DAPND) 2917 qid.qtype |= QTAPPEND; 2918 if(mode & DLOCK) 2919 qid.qtype |= QTEXCL; 2920 return qid; 2921} 2922 2923dir9p2(d: ref Dentry): Sys->Dir 2924{ 2925 dir: Sys->Dir; 2926 2927 dir.name = d.name; 2928 dir.uid = uidtostr(d.uid); 2929 dir.gid = uidtostr(d.gid); 2930 dir.muid = uidtostr(d.muid); 2931 dir.qid = d.qid; 2932 dir.mode = d.mode & 8r777; 2933 if(d.mode & DDIR) 2934 dir.mode |= DMDIR; 2935 if(d.mode & DAPND) 2936 dir.mode |= DMAPPEND; 2937 if(d.mode & DLOCK) 2938 dir.mode |= DMEXCL; 2939 dir.atime = d.atime; 2940 dir.mtime = d.mtime; 2941 dir.length = big d.size; 2942 dir.dtype = 0; 2943 dir.dev = 0; 2944 return dir; 2945} 2946 2947rootream(dev: ref Device, addr: int) 2948{ 2949 p := Iobuf.get(dev, addr, Bmod|Bimm); 2950 p.iobuf[0:] = emptyblock; 2951 p.settag(Tdir, QPROOT); 2952 d := Dentry.get(p, 0); 2953 d.name = "/"; 2954 d.uid = -1; 2955 d.gid = -1; 2956 d.mode = DALLOC | DDIR | 2957 ((DREAD|DWRITE|DEXEC) << 6) | 2958 ((DREAD|DWRITE|DEXEC) << 3) | 2959 ((DREAD|DWRITE|DEXEC) << 0); 2960 d.qid.path = big QPROOT; 2961 d.qid.vers = 0; 2962 d.qid.qtype = QTDIR; 2963 d.atime = now(); 2964 d.mtime = d.atime; 2965 d.change(~0); 2966 d.access(FREAD|FWRITE, -1); 2967 d.update(); 2968 p.put(); 2969} 2970 2971superream(dev: ref Device, addr: int) 2972{ 2973 fsize := wrensize(dev); 2974 if(fsize <= 0) 2975 panic("file system device size"); 2976 p := Iobuf.get(dev, addr, Bmod|Bimm); 2977 p.iobuf[0:] = emptyblock; 2978 p.settag(Tsuper, QPSUPER); 2979 sb := ref Superb; 2980 sb.iob = p; 2981 sb.fstart = 1; 2982 sb.fsize = fsize; 2983 sb.qidgen = 10; 2984 sb.tfree = 0; 2985 sb.fsok = 0; 2986 sb.fbuf = p.iobuf[Super1size:]; 2987 put4(sb.fbuf, 0, 1); # nfree = 1 2988 for(i := fsize-1; i>=addr+2; i--) 2989 addfree(dev, i, sb); 2990 sb.put(); 2991} 2992 2993eprint(s: string) 2994{ 2995 sys->print("kfs: %s\n", s); 2996} 2997 2998# 2999# /adm/users 3000# 3001# uid:user:leader:members[,...] 3002 3003User: adt { 3004 uid: int; 3005 name: string; 3006 leader: int; 3007 mem: list of int; 3008}; 3009 3010users: list of ref User; 3011 3012admusers := array[] of { 3013 (-1, "adm", "adm"), 3014 (None, "none", "adm"), 3015 (Noworld, "noworld", nil), 3016 (10000, "sys", nil), 3017 (10001, "upas", "upas"), 3018 (10002, "bootes", "bootes"), 3019 (10006, "inferno", nil), 3020}; 3021 3022userinit() 3023{ 3024 if(!cmd_users() && users == nil){ 3025 cprint("initializing minimal user table"); 3026 defaultusers(); 3027 } 3028 writegroup = strtouid("write"); 3029} 3030 3031cmd_users(): int 3032{ 3033 if(kopen(FID1, FID2, array[] of {"adm", "users"}, OREAD) != nil) 3034 return 0; 3035 buf: array of byte; 3036 for(off := 0;;){ 3037 (a, e) := kread(FID2, off, Styx->MAXFDATA); 3038 if(e != nil){ 3039 cprint("/adm/users read error: "+e); 3040 return 0; 3041 } 3042 if(len a == 0) 3043 break; 3044 off += len a; 3045 if(buf != nil){ 3046 c := array[len buf + len a] of byte; 3047 if(buf != nil) 3048 c[0:] = buf; 3049 c[len buf:] = a; 3050 buf = c; 3051 }else 3052 buf = a; 3053 } 3054 kclose(FID2); 3055 3056 # (uid:name:lead:mem,...\n)+ 3057 (nl, lines) := sys->tokenize(string buf, "\n"); 3058 if(nl == 0){ 3059 cprint("empty /adm/users"); 3060 return 0; 3061 } 3062 oldusers := users; 3063 users = nil; 3064 3065 # first pass: enter id:name 3066 for(l := lines; l != nil; l = tl l){ 3067 uid, name, r: string; 3068 s := hd l; 3069 if(s == "" || s[0] == '#') 3070 continue; 3071 (uid, r) = field(s, ':'); 3072 (name, r) = field(r, ':'); 3073 if(uid == nil || name == nil || string int uid != uid){ 3074 cprint("invalid /adm/users line: "+hd l); 3075 users = oldusers; 3076 return 0; 3077 } 3078 adduser(int uid, name, nil, nil); 3079 } 3080 3081 # second pass: groups and leaders 3082 for(l = lines; l != nil; l = tl l){ 3083 s := hd l; 3084 if(s == "" || s[0] == '#') 3085 continue; 3086 name, lead, mem, r: string; 3087 (nil, r) = field(s, ':'); # skip id 3088 (name, r) = field(r, ':'); 3089 (lead, mem) = field(r, ':'); 3090 (nil, mems) := sys->tokenize(mem, ",\n"); 3091 if(name == nil || lead == nil && mems == nil) 3092 continue; 3093 u := finduname(name); 3094 if(lead != nil){ 3095 lu := strtouid(lead); 3096 if(lu != None) 3097 u.leader = lu; 3098 else if(lead != nil) 3099 u.leader = u.uid; # mimic kfs not fs 3100 } 3101 mids: list of int = nil; 3102 for(; mems != nil; mems = tl mems){ 3103 lu := strtouid(hd mems); 3104 if(lu != None) 3105 mids = lu :: mids; 3106 } 3107 u.mem = mids; 3108 } 3109 3110 if(debug) 3111 for(x := users; x != nil; x = tl x){ 3112 u := hd x; 3113 sys->print("%d : %q : %d :", u.uid, u.name, u.leader); 3114 for(y := u.mem; y != nil; y = tl y) 3115 sys->print(" %d", hd y); 3116 sys->print("\n"); 3117 } 3118 return 1; 3119} 3120 3121field(s: string, c: int): (string, string) 3122{ 3123 for(i := 0; i < len s; i++) 3124 if(s[i] == c) 3125 return (s[0:i], s[i+1:]); 3126 return (s, nil); 3127} 3128 3129defaultusers() 3130{ 3131 for(i := 0; i < len admusers; i++){ 3132 (id, name, leader) := admusers[i]; 3133 adduser(id, name, leader, nil); 3134 } 3135} 3136 3137finduname(s: string): ref User 3138{ 3139 for(l := users; l != nil; l = tl l){ 3140 u := hd l; 3141 if(u.name == s) 3142 return u; 3143 } 3144 return nil; 3145} 3146 3147uidtostr(id: int): string 3148{ 3149 if(id == None) 3150 return "none"; 3151 for(l := users; l != nil; l = tl l){ 3152 u := hd l; 3153 if(u.uid == id) 3154 return u.name; 3155 } 3156 return sys->sprint("#%d", id); 3157} 3158 3159leadgroup(ui: int, gi: int): int 3160{ 3161 for(l := users; l != nil; l = tl l){ 3162 u := hd l; 3163 if(u.uid == gi){ 3164 if(u.leader == ui) 3165 return 1; 3166 if(u.leader == 0) 3167 return ingroup(ui, gi); 3168 return 0; 3169 } 3170 } 3171 return 0; 3172} 3173 3174strtouid(s: string): int 3175{ 3176 if(s == "none") 3177 return None; 3178 u := finduname(s); 3179 if(u != nil) 3180 return u.uid; 3181 return 0; 3182} 3183 3184ingroup(uid: int, gid: int): int 3185{ 3186 if(uid == gid) 3187 return 1; 3188 for(l := users; l != nil; l = tl l){ 3189 u := hd l; 3190 if(u.uid == gid){ 3191 for(m := u.mem; m != nil; m = tl m) 3192 if(hd m == uid) 3193 return 1; 3194 return 0; 3195 } 3196 } 3197 return 0; 3198} 3199 3200baduname(s: string): int 3201{ 3202 n := checkname9p2(s); 3203 if(n == 0 || n+1 > NAMELEN || s == "." || s == ".."){ 3204 sys->print("kfs: illegal user name %q\n", s); 3205 return 1; 3206 } 3207 return 0; 3208} 3209 3210adduser(id: int, name: string, leader: string, mem: list of string) 3211{ 3212 if(baduname(name)) 3213 return; 3214 for(l := users; l != nil; l = tl l){ 3215 u := hd l; 3216 if(u.uid == id){ 3217 sys->print("kfs: duplicate user ID %d (name %q)\n", id, u.name); 3218 return; 3219 }else if(u.name == name){ 3220 sys->print("kfs: duplicate user name %q (id %d)\n", name, u.uid); 3221 return; 3222 } 3223 } 3224 if(name == leader) 3225 lid := id; 3226 else if(leader == nil) 3227 lid = 0; 3228 else if(!baduname(leader)) 3229 lid = strtouid(leader); 3230 else 3231 return; 3232 memid: list of int; 3233 for(; mem != nil; mem = tl mem){ 3234 if(baduname(hd mem)) 3235 return; 3236 x := strtouid(hd mem); 3237 if(x != 0) 3238 memid = x :: memid; 3239 } 3240 u := ref User(id, name, lid, memid); 3241 users = u :: users; 3242} 3243 3244Lock.new(): ref Lock 3245{ 3246 return ref Lock(chan[1] of int); 3247} 3248 3249Lock.lock(l: self ref Lock) 3250{ 3251 l.c <-= 1; 3252} 3253 3254Lock.canlock(l: self ref Lock): int 3255{ 3256 alt{ 3257 l.c <-= 1 => 3258 return 1; 3259 * => 3260 return 0; 3261 } 3262} 3263 3264Lock.unlock(l: self ref Lock) 3265{ 3266 <-l.c; 3267} 3268 3269# 3270# kfs check, could be a separate module if that seemed important 3271# 3272 3273MAXDEPTH: con 100; 3274MAXNAME: con 4000; 3275 3276Map: adt { 3277 lo, hi: int; 3278 bits: array of byte; 3279 nbad: int; 3280 ndup: int; 3281 nmark: int; 3282 3283 new: fn(lo, hi: int): ref Map; 3284 isset: fn(b: self ref Map, a: int): int; 3285 mark: fn(b: self ref Map, a: int): string; 3286}; 3287 3288Check: adt { 3289 dev: ref Device; 3290 3291 amap: ref Map; 3292 qmap: ref Map; 3293 3294 name: string; 3295 nfiles: int; 3296 maxq: int; 3297 3298 mod: int; 3299 flags: int; 3300 oldblock: int; 3301 3302 depth: int; 3303 maxdepth: int; 3304 3305 check: fn(c: self ref Check); 3306 touch: fn(c: self ref Check, a: int): int; 3307 checkdir: fn(c: self ref Check, a: int, qpath: int): int; 3308 checkindir: fn(c: self ref Check, a: int, d: ref Dentry, qpath: int): int; 3309 maked: fn(c: self ref Check, a: int, s: int, qpath: int): ref Dentry; 3310 modd: fn(c: self ref Check, a: int, s: int, d: ref Dentry); 3311 fsck: fn(c: self ref Check, d: ref Dentry): int; 3312 xread: fn(c: self ref Check, a: int, qpath: int); 3313 xtag: fn(c: self ref Check, a: int, tag: int, qpath: int): ref Iobuf; 3314 ckfreelist: fn(c: self ref Check, sb: ref Superb); 3315 mkfreelist: fn(c: self ref Check, sb: ref Superb); 3316 amark: fn(c: self ref Check, a: int): int; 3317 fmark: fn(c: self ref Check, a: int): int; 3318 missing: fn(c: self ref Check, sb: ref Superb); 3319 qmark: fn(c: self ref Check, q: int); 3320}; 3321 3322check(dev: ref Device, flag: int) 3323{ 3324 #mainlock.wlock(); 3325 #mainlock.wunlock(); 3326 c := ref Check; 3327 c.dev = dev; 3328 c.nfiles = 0; 3329 c.maxq = 0; 3330 c.mod = 0; 3331 c.flags = flag; 3332 c.oldblock = 0; 3333 c.depth = 0; 3334 c.maxdepth = 0; 3335 c.check(); 3336} 3337 3338checkflags(s: string): int 3339{ 3340 f := 0; 3341 for(i := 0; i < len s; i++) 3342 case s[i] { 3343 'r' => f |= Crdall; 3344 't' => f |= Ctag; 3345 'P' => f |= Cpfile; 3346 'p' => f |= Cpdir; 3347 'f' => f |= Cfree; 3348 'c' => f |= Cream; 3349 'd' => f |= Cbad; 3350 'w' => f |= Ctouch; 3351 'q' => f |= Cquiet; 3352 'v' => ; # old verbose flag; ignored 3353 * => return -1; 3354 } 3355 return f; 3356} 3357 3358Check.check(c: self ref Check) 3359{ 3360 sbaddr := SUPERADDR; 3361 p := c.xtag(sbaddr, Tsuper, QPSUPER); 3362 if(p == nil){ 3363 cprint(sys->sprint("bad superblock")); 3364 return; 3365 } 3366 sb := Superb.unpack(p.iobuf); 3367 sb.iob = p; 3368 3369 fstart := sb.fstart; 3370 if(fstart != 1){ 3371 cprint(sys->sprint("invalid superblock")); 3372 return; 3373 } 3374 fsize := sb.fsize; 3375 if(fsize < fstart || fsize > wrensize(c.dev)){ 3376 cprint(sys->sprint("invalid size in superblock")); 3377 return; 3378 } 3379 c.amap = Map.new(fstart, fsize); 3380 3381 nqid := sb.qidgen+100; # not as much of a botch 3382 if(nqid > 1024*1024*8) 3383 nqid = 1024*1024*8; 3384 if(nqid < 64*1024) 3385 nqid = 64*1024; 3386 c.qmap = Map.new(0, nqid); 3387 3388 c.mod = 0; 3389 c.depth = 0; 3390 c.maxdepth = 0; 3391 3392 if(c.amark(sbaddr)) 3393 {} 3394 3395 if(!(c.flags & Cquiet)) 3396 cprint(sys->sprint("checking file system: %s", "main")); 3397 c.nfiles = 0; 3398 c.maxq = 0; 3399 3400 d := c.maked(ROOTADDR, 0, QPROOT); 3401 if(d != nil){ 3402 if(c.amark(ROOTADDR)) 3403 {} 3404 if(c.fsck(d)) 3405 c.modd(ROOTADDR, 0, d); 3406 if(--c.depth != 0) 3407 cprint("depth not zero on return"); 3408 } 3409 if(sb.qidgen < c.maxq) 3410 cprint(sys->sprint("qid generator low path=%d maxq=%d", sb.qidgen, c.maxq)); 3411 3412 nqbad := c.qmap.nbad + c.qmap.ndup; 3413 c.qmap = nil; # could use to implement resequence 3414 3415 ndup := c.amap.ndup; 3416 nused := c.amap.nmark; 3417 3418 c.amap.ndup = c.amap.nmark = 0; # reset for free list counts 3419 if(c.flags & Cfree){ 3420 c.name = "free list"; 3421 c.mkfreelist(sb); 3422 sb.qidgen = c.maxq; 3423 p.settag(Tsuper, QPNONE); 3424 }else 3425 c.ckfreelist(sb); 3426 3427 nbad := c.amap.nbad; 3428 nfdup := c.amap.ndup; 3429 nfree := c.amap.nmark; 3430 # leave amap for missing, below 3431 3432 if(c.mod){ 3433 cprint("file system was modified"); 3434 p.settag(Tsuper, QPNONE); 3435 } 3436 3437 if(!(c.flags & Cquiet)){ 3438 cprint(sys->sprint("%8d files", c.nfiles)); 3439 cprint(sys->sprint("%8d blocks in the file system", fsize-fstart)); 3440 cprint(sys->sprint("%8d used blocks", nused)); 3441 cprint(sys->sprint("%8d free blocks", sb.tfree)); 3442 } 3443 if(!(c.flags & Cfree)){ 3444 if(nfree != sb.tfree) 3445 cprint(sys->sprint("%8d free blocks found", nfree)); 3446 if(nfdup) 3447 cprint(sys->sprint("%8d blocks duplicated in the free list", nfdup)); 3448 if(fsize-fstart-nused-nfree) 3449 cprint(sys->sprint("%8d missing blocks", fsize-fstart-nused-nfree)); 3450 } 3451 if(ndup) 3452 cprint(sys->sprint("%8d address duplications", ndup)); 3453 if(nbad) 3454 cprint(sys->sprint("%8d bad block addresses", nbad)); 3455 if(nqbad) 3456 cprint(sys->sprint("%8d bad qids", nqbad)); 3457 if(!(c.flags & Cquiet)) 3458 cprint(sys->sprint("%8d maximum qid path", c.maxq)); 3459 c.missing(sb); 3460 3461 sb.put(); 3462} 3463 3464Check.touch(c: self ref Check, a: int): int 3465{ 3466 if((c.flags&Ctouch) && a){ 3467 p := Iobuf.get(c.dev, a, Bread|Bmod); 3468 if(p != nil) 3469 p.put(); 3470 return 1; 3471 } 3472 return 0; 3473} 3474 3475Check.checkdir(c: self ref Check, a: int, qpath: int): int 3476{ 3477 ns := len c.name; 3478 dmod := c.touch(a); 3479 for(i:=0; i<DIRPERBUF; i++){ 3480 nd := c.maked(a, i, qpath); 3481 if(nd == nil) 3482 break; 3483 if(c.fsck(nd)){ 3484 c.modd(a, i, nd); 3485 dmod++; 3486 } 3487 c.depth--; 3488 c.name = c.name[0:ns]; 3489 } 3490 c.name = c.name[0:ns]; 3491 return dmod; 3492} 3493 3494Check.checkindir(c: self ref Check, a: int, d: ref Dentry, qpath: int): int 3495{ 3496 dmod := c.touch(a); 3497 p := c.xtag(a, Tind1, qpath); 3498 if(p == nil) 3499 return dmod; 3500 for(i:=0; i<INDPERBUF; i++){ 3501 a = get4(p.iobuf, i*4); 3502 if(a == 0) 3503 continue; 3504 if(c.amark(a)){ 3505 if(c.flags & Cbad){ 3506 put4(p.iobuf, i*4, 0); 3507 p.flags |= Bmod; 3508 } 3509 continue; 3510 } 3511 if(d.mode & DDIR) 3512 dmod += c.checkdir(a, qpath); 3513 else if(c.flags & Crdall) 3514 c.xread(a, qpath); 3515 } 3516 p.put(); 3517 return dmod; 3518} 3519 3520Check.fsck(c: self ref Check, d: ref Dentry): int 3521{ 3522 p: ref Iobuf; 3523 i: int; 3524 a, qpath: int; 3525 3526 if(++c.depth >= c.maxdepth){ 3527 c.maxdepth = c.depth; 3528 if(c.maxdepth >= MAXDEPTH){ 3529 cprint(sys->sprint("max depth exceeded: %s", c.name)); 3530 return 0; 3531 } 3532 } 3533 dmod := 0; 3534 if(!(d.mode & DALLOC)) 3535 return 0; 3536 c.nfiles++; 3537 3538 ns := len c.name; 3539 i = styx->utflen(d.name); 3540 if(i >= NAMELEN){ 3541 d.name[NAMELEN-1] = 0; # TO DO: not quite right 3542 cprint(sys->sprint("%q.name (%q) not terminated", c.name, d.name)); 3543 return 0; 3544 } 3545 ns += i; 3546 if(ns >= MAXNAME){ 3547 cprint(sys->sprint("%q.name (%q) name too large", c.name, d.name)); 3548 return 0; 3549 } 3550 c.name += d.name; 3551 3552 if(d.mode & DDIR){ 3553 if(ns > 1) 3554 c.name += "/"; 3555 if(c.flags & Cpdir) 3556 cprint(sys->sprint("%s", c.name)); 3557 } else if(c.flags & Cpfile) 3558 cprint(sys->sprint("%s", c.name)); 3559 3560 qpath = int d.qid.path & ~QPDIR; 3561 c.qmark(qpath); 3562 if(qpath > c.maxq) 3563 c.maxq = qpath; 3564 for(i=0; i<NDBLOCK; i++){ 3565 a = get4(d.buf, Odblock+i*4); 3566 if(a == 0) 3567 continue; 3568 if(c.amark(a)){ 3569 put4(d.buf, Odblock+i*4, 0); 3570 dmod++; 3571 continue; 3572 } 3573 if(d.mode & DDIR) 3574 dmod += c.checkdir(a, qpath); 3575 else if(c.flags & Crdall) 3576 c.xread(a, qpath); 3577 } 3578 a = get4(d.buf, Oiblock); 3579 if(a){ 3580 if(c.amark(a)){ 3581 put4(d.buf, Oiblock, 0); 3582 dmod++; 3583 } 3584 else 3585 dmod += c.checkindir(a, d, qpath); 3586 } 3587 3588 a = get4(d.buf, Odiblock); 3589 if(a && c.amark(a)){ 3590 put4(d.buf, Odiblock, 0); 3591 return dmod + 1; 3592 } 3593 dmod += c.touch(a); 3594 p = c.xtag(a, Tind2, qpath); 3595 if(p != nil){ 3596 for(i=0; i<INDPERBUF; i++){ 3597 a = get4(p.iobuf, i*4); 3598 if(a == 0) 3599 continue; 3600 if(c.amark(a)){ 3601 if(c.flags & Cbad){ 3602 put4(p.iobuf, i*4, 0); 3603 p.flags |= Bmod; 3604 } 3605 continue; 3606 } 3607 dmod += c.checkindir(a, d, qpath); 3608 } 3609 p.put(); 3610 } 3611 return dmod; 3612} 3613 3614Check.ckfreelist(c: self ref Check, sb: ref Superb) 3615{ 3616 c.name = "free list"; 3617 cprint(sys->sprint("check %s", c.name)); 3618 fb := sb.fbuf; 3619 a := SUPERADDR; 3620 p: ref Iobuf; 3621 lo := 0; 3622 hi := 0; 3623 for(;;){ 3624 n := get4(fb, 0); # nfree 3625 if(n < 0 || n > FEPERBUF){ 3626 cprint(sys->sprint("check: nfree bad %d", a)); 3627 break; 3628 } 3629 for(i:=1; i<n; i++){ 3630 a = get4(fb, 4+i*4); # free[i] 3631 if(a && !c.fmark(a)){ 3632 if(!lo || lo > a) 3633 lo = a; 3634 if(!hi || hi < a) 3635 hi = a; 3636 } 3637 } 3638 a = get4(fb, 4); # free[0] 3639 if(a == 0) 3640 break; 3641 if(c.fmark(a)) 3642 break; 3643 if(!lo || lo > a) 3644 lo = a; 3645 if(!hi || hi < a) 3646 hi = a; 3647 if(p != nil) 3648 p.put(); 3649 p = c.xtag(a, Tfree, QPNONE); 3650 if(p == nil) 3651 break; 3652 fb = p.iobuf; 3653 } 3654 if(p != nil) 3655 p.put(); 3656 cprint(sys->sprint("lo = %d; hi = %d", lo, hi)); 3657} 3658 3659# 3660# make freelist from scratch 3661# 3662Check.mkfreelist(c: self ref Check, sb: ref Superb) 3663{ 3664 sb.fbuf[0:] = emptyblock[0:(FEPERBUF+1)*4]; 3665 sb.tfree = 0; 3666 put4(sb.fbuf, 0, 1); # nfree = 1 3667 for(a:=sb.fsize-sb.fstart-1; a >= 0; a--){ 3668 i := a>>3; 3669 if(i < 0 || i >= len c.amap.bits) 3670 continue; 3671 b := byte (1 << (a&7)); 3672 if((c.amap.bits[i] & b) != byte 0) 3673 continue; 3674 addfree(c.dev, sb.fstart+a, sb); 3675 c.amap.bits[i] |= b; 3676 } 3677 sb.iob.flags |= Bmod; 3678} 3679 3680# 3681# makes a copy of a Dentry's representation on disc so that 3682# the rest of the much larger iobuf can be freed. 3683# 3684Check.maked(c: self ref Check, a: int, s: int, qpath: int): ref Dentry 3685{ 3686 p := c.xtag(a, Tdir, qpath); 3687 if(p == nil) 3688 return nil; 3689 d := Dentry.get(p, s); 3690 if(d == nil) 3691 return nil; 3692 copy := array[len d.buf] of byte; 3693 copy[0:] = d.buf; 3694 d.put(); 3695 d.buf = copy; 3696 return d; 3697} 3698 3699Check.modd(c: self ref Check, a: int, s: int, d1: ref Dentry) 3700{ 3701 if(!(c.flags & Cbad)) 3702 return; 3703 p := Iobuf.get(c.dev, a, Bread); 3704 d := Dentry.get(p, s); 3705 if(d == nil){ 3706 if(p != nil) 3707 p.put(); 3708 return; 3709 } 3710 d.buf[0:] = d1.buf; 3711 p.flags |= Bmod; 3712 p.put(); 3713} 3714 3715Check.xread(c: self ref Check, a: int, qpath: int) 3716{ 3717 p := c.xtag(a, Tfile, qpath); 3718 if(p != nil) 3719 p.put(); 3720} 3721 3722Check.xtag(c: self ref Check, a: int, tag: int, qpath: int): ref Iobuf 3723{ 3724 if(a == 0) 3725 return nil; 3726 p := Iobuf.get(c.dev, a, Bread); 3727 if(p == nil){ 3728 cprint(sys->sprint("check: \"%s\": xtag: p null", c.name)); 3729 if(c.flags & (Cream|Ctag)){ 3730 p = Iobuf.get(c.dev, a, Bmod); 3731 if(p != nil){ 3732 p.iobuf[0:] = emptyblock; 3733 p.settag(tag, qpath); 3734 c.mod++; 3735 return p; 3736 } 3737 } 3738 return nil; 3739 } 3740 if(p.checktag(tag, qpath)){ 3741 cprint(sys->sprint("check: \"%s\": xtag: checktag", c.name)); 3742 if(c.flags & Cream) 3743 p.iobuf[0:] = emptyblock; 3744 if(c.flags & (Cream|Ctag)){ 3745 p.settag(tag, qpath); 3746 c.mod++; 3747 } 3748 return p; 3749 } 3750 return p; 3751} 3752 3753Check.amark(c: self ref Check, a: int): int 3754{ 3755 e := c.amap.mark(a); 3756 if(e != nil){ 3757 cprint(sys->sprint("check: \"%s\": %s %d", c.name, e, a)); 3758 return e != "dup"; # don't clear dup blocks because rm might repair 3759 } 3760 return 0; 3761} 3762 3763Check.fmark(c: self ref Check,a: int): int 3764{ 3765 e := c.amap.mark(a); 3766 if(e != nil){ 3767 cprint(sys->sprint("check: \"%s\": %s %d", c.name, e, a)); 3768 return 1; 3769 } 3770 return 0; 3771} 3772 3773Check.missing(c: self ref Check, sb: ref Superb) 3774{ 3775 n := 0; 3776 for(a:=sb.fsize-sb.fstart-1; a>=0; a--){ 3777 i := a>>3; 3778 b := byte (1 << (a&7)); 3779 if((c.amap.bits[i] & b) == byte 0){ 3780 cprint(sys->sprint("missing: %d", sb.fstart+a)); 3781 n++; 3782 } 3783 if(n > 10){ 3784 cprint(sys->sprint(" ...")); 3785 break; 3786 } 3787 } 3788} 3789 3790Check.qmark(c: self ref Check, qpath: int) 3791{ 3792 e := c.qmap.mark(qpath); 3793 if(e != nil){ 3794 if(c.qmap.nbad+c.qmap.ndup < 20) 3795 cprint(sys->sprint("check: \"%s\": qid %s 0x%ux", c.name, e, qpath)); 3796 } 3797} 3798 3799Map.new(lo, hi: int): ref Map 3800{ 3801 m := ref Map; 3802 n := (hi-lo+7)>>3; 3803 m.bits = array[n] of {* => byte 0}; 3804 m.lo = lo; 3805 m.hi = hi; 3806 m.nbad = 0; 3807 m.ndup = 0; 3808 m.nmark = 0; 3809 return m; 3810} 3811 3812Map.isset(m: self ref Map, i: int): int 3813{ 3814 if(i < m.lo || i >= m.hi) 3815 return -1; # hard to say 3816 i -= m.lo; 3817 return (m.bits[i>>3] & byte (1<<(i&7))) != byte 0; 3818} 3819 3820Map.mark(m: self ref Map, i: int): string 3821{ 3822 if(i < m.lo || i >= m.hi){ 3823 m.nbad++; 3824 return "out of range"; 3825 } 3826 i -= m.lo; 3827 b := byte (1 << (i&7)); 3828 i >>= 3; 3829 if((m.bits[i] & b) != byte 0){ 3830 m.ndup++; 3831 return "dup"; 3832 } 3833 m.bits[i] |= b; 3834 m.nmark++; 3835 return nil; 3836} 3837 3838cprint(s: string) 3839{ 3840 if(consoleout != nil) 3841 consoleout <-= s+"\n"; 3842 else 3843 eprint(s); 3844} 3845