implement Kfs; ## Copyright © 1991-2003 Lucent Technologies Inc.# Limbo version Copyright © 2004 Vita Nuova Holdings Limited# ## TO DO:# - sync proc; Bmod; process structure# - swiz? include "sys.m"; sys: Sys; Qid, Dir: import Sys; DMEXCL, DMAPPEND, DMDIR: import Sys; QTEXCL, QTAPPEND, QTDIR: import Sys; include "draw.m"; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; NOFID, OEXEC, ORCLOSE, OREAD, OWRITE, ORDWR, OTRUNC: import Styx; IOHDRSZ: import Styx; include "daytime.m"; daytime: Daytime; now: import daytime; include "arg.m"; Kfs: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; MAXBUFSIZE: con 16*1024; ## fundamental constants#NAMELEN: con 28; # size of names, including null byteNDBLOCK: con 6; # number of direct blocks in DentryMAXFILESIZE: con big 16r7FFFFFFF; # Plan 9's limit (kfs's size is signed) SUPERADDR: con 1;ROOTADDR: con 2; QPDIR: con int (1<<31);QPNONE: con 0;QPROOT: con 1;QPSUPER: con 2; ## don't change, these are the mode bits on disc#DALLOC: con 16r8000;DDIR: con 16r4000;DAPND: con 16r2000;DLOCK: con 16r1000;DREAD: con 4;DWRITE: con 2;DEXEC: con 1; ## other constants# MINUTE: con 60;TLOCK: con 5*MINUTE;NTLOCK: con 200; # number of active file locks Buffering: con 1; FID1, FID2, FID3: con 1+iota; None: con 0; # user ID for "none"Noworld: con 9999; # conventional id for "noworld" group Lock: adt{ c: chan of int; new: fn(): ref Lock; lock: fn(c: self ref Lock); canlock: fn(c: self ref Lock): int; unlock: fn(c: self ref Lock);}; Dentry: adt{ name: string; uid: int; gid: int; muid: int; # not set by plan 9's kfs mode: int; # mode bits on disc: DALLOC etc qid: Qid; # 9p1 format on disc size: big; # only 32-bits on disc, and Plan 9 limits it to signed atime: int; mtime: int; iob: ref Iobuf; # locked block containing directory entry, when in memory buf: array of byte; # pointer into block to packed directory entry, when in memory mod: int; # bits of buf that need updating unpack: fn(a: array of byte): ref Dentry; get: fn(p: ref Iobuf, slot: int): ref Dentry; geta: fn(d: ref Device, addr: int, slot: int, qpath: int, mode: int): (ref Dentry, string); getd: fn(f: ref File, mode: int): (ref Dentry, string); put: fn(d: self ref Dentry); access: fn(d: self ref Dentry, f: int, uid: int); change: fn(d: self ref Dentry, f: int); release: fn(d: self ref Dentry); getblk: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; getblk1: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; rel2abs: fn(d: self ref Dentry, a: int, tag: int, putb: int): int; trunc: fn(d: self ref Dentry, uid: int); update: fn(d: self ref Dentry); print: fn(d: self ref Dentry);}; Uname, Uids, Umode, Uqid, Usize, Utime: con 1<<iota; # Dentry.mod ## disc structure:# Tag: pad[2] tag[2] path[4]Tagsize: con 2+2+4; Tag: adt{ tag: int; path: int; unpack: fn(a: array of byte): Tag; pack: fn(t: self Tag, a: array of byte);}; Superb: adt{ iob: ref Iobuf; fstart: int; fsize: int; tfree: int; qidgen: int; # generator for unique ids fsok: int; fbuf: array of byte; # nfree[4] free[FEPERBLK*4]; aliased into containing block get: fn(dev: ref Device, flags: int): ref Superb; touched: fn(s: self ref Superb); put: fn(s: self ref Superb); print: fn(s: self ref Superb); pack: fn(s: self ref Superb, a: array of byte); unpack: fn(a: array of byte): ref Superb;}; Device: adt{ fd: ref Sys->FD; ronly: int; # could put locks here if necessary # partitioning by ds(3)}; ## one for each locked qid#Tlock: adt{ dev: ref Device; time: int; qpath: int; file: cyclic ref File; # TO DO: probably not needed}; File: adt{ qlock: chan of int; qid: Qid; wpath: ref Wpath; tlock: cyclic ref Tlock; # if file is locked fs: ref Device; addr: int; slot: int; lastra: int; # read ahead address fid: int; uid: int; open: int; cons: int; # if opened by console doffset: big; # directory reading dvers: int; dslot: int; new: fn(fid: int): ref File; access: fn(f: self ref File, d: ref Dentry, mode: int): int; lock: fn(f: self ref File); unlock: fn(f: self ref File);}; FREAD, FWRITE, FREMOV, FWSTAT: con 1<<iota; # File.open Chan: adt{ fd: ref Sys->FD; # fd request came in on# rlock, wlock: QLock; # lock for reading/writing messages on cp flags: int; flist: list of ref File; # active files fqlock: chan of int;# reflock: RWLock; # lock for Tflush msize: int; # version new: fn(fd: ref Sys->FD): ref Chan; getfid: fn(c: self ref Chan, fid: int, flag: int): ref File; putfid: fn(c: self ref Chan, f: ref File); flock: fn(nil: self ref Chan); funlock: fn(nil: self ref Chan);}; Hiob: adt{ link: ref Iobuf; # TO DO: eliminate circular list lk: ref Lock; niob: int; newbuf: fn(h: self ref Hiob): ref Iobuf;}; Iobuf: adt{ qlock: chan of int; dev: ref Device; fore: cyclic ref Iobuf; # lru hash chain back: cyclic ref Iobuf; # for lru iobuf: array of byte; # only active while locked xiobuf: array of byte; # "real" buffer pointer addr: int; flags: int; get: fn(dev: ref Device, addr: int, flags: int):ref Iobuf; put: fn(iob: self ref Iobuf); lock: fn(iob: self ref Iobuf); canlock: fn(iob: self ref Iobuf): int; unlock: fn(iob: self ref Iobuf); checktag: fn(iob: self ref Iobuf, tag: int, qpath: int): int; settag: fn(iob: self ref Iobuf, tag: int, qpath: int);}; Wpath: adt{ up: cyclic ref Wpath; # pointer upwards in path addr: int; # directory entry addr slot: int; # directory entry slot}; ## error codes generated from the file server#Eaccess: con "access permission denied";Ealloc: con "phase error -- directory entry not allocated";Eauth: con "authentication failed";Eauthmsg: con "kfs: authentication not required";Ebadspc: con "attach -- bad specifier";Ebadu: con "attach -- privileged user";Ebroken: con "close/read/write -- lock is broken";Echar: con "bad character in directory name";Econvert: con "protocol botch";Ecount: con "read/write -- count too big";Edir1: con "walk -- in a non-directory";Edir2: con "create -- in a non-directory";Edot: con "create -- . and .. illegal names";Eempty: con "remove -- directory not empty";Eentry: con "directory entry not found";Eexist: con "create -- file exists";Efid: con "unknown fid";Efidinuse: con "fid already in use";Efull: con "file system full";Elocked: con "open/create -- file is locked";Emode: con "open/create -- unknown mode";Ename: con "create/wstat -- bad character in file name";Enotd: con "wstat -- attempt to change directory";Enotg: con "wstat -- not in group";Enotl: con "wstat -- attempt to change length";Enotm: con "wstat -- unknown type/mode";Enotu: con "wstat -- not owner";Eoffset: con "read/write -- offset negative";Eopen: con "read/write -- on non open fid";Ephase: con "phase error -- cannot happen";Eqid: con "phase error -- qid does not match";Eqidmode: con "wstat -- qid.qtype/dir.mode mismatch";Eronly: con "file system read only";Ersc: con "it's russ's fault. bug him.";Esystem: con "kfs system error";Etoolong: con "name too long";Etoobig: con "write -- file size limit";Ewalk: con "walk -- too many (system wide)"; ## tags on block#Tnone,Tsuper, # the super blockTdir, # directory contentsTind1, # points to blocksTind2, # points to Tind1Tfile, # file contentsTfree, # in free listTbuck, # cache fs bucketTvirgo, # fake worm virgin bitsTcache, # cw cache thingsMAXTAG: con iota; ## flags to Iobuf.get# Bread, # read the block if miss Bprobe, # return null if miss Bmod, # set modified bit in buffer Bimm, # set immediate bit in buffer Bres: # never renamed con 1<<iota; ## check flags# Crdall, # read all files Ctag, # rebuild tags Cpfile, # print files Cpdir, # print directories Cfree, # rebuild free list Cream, # clear all bad tags Cbad, # clear all bad blocks Ctouch, # touch old dir and indir Cquiet: # report just nasty things con 1<<iota; ## buffer size variables, determined by RBUFSIZE#RBUFSIZE: int;BUFSIZE: int;DIRPERBUF: int;INDPERBUF: int;INDPERBUF2: int;FEPERBUF: int; emptyblock: array of byte; wrenfd: ref Sys->FD;thedevice: ref Device;devnone: ref Device;wstatallow := 0;writeallow := 0;writegroup := 0; ream := 0;readonly := 0;noatime := 0;localfs: con 1;conschan: ref Chan;consuid := -1;consgid := -1;debug := 0;kfsname: string;consoleout: chan of string;mainlock: ref Lock;pids: list of int; noqid: Qid; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; styx = load Styx Styx->PATH; daytime = load Daytime Daytime->PATH; styx->init(); arg := load Arg Arg->PATH; if(arg == nil) error(sys->sprint("can't load %s: %r", Arg->PATH)); arg->init(args); arg->setusage("disk/kfs [-r [-b bufsize]] [-cADPRW] [-n name] kfsfile"); bufsize := 1024; nocheck := 0; while((o := arg->opt()) != 0) case o { 'c' => nocheck = 1; 'r' => ream = 1; 'b' => bufsize = int arg->earg(); 'D' => debug = !debug; 'P' => writeallow = 1; 'W' => wstatallow = 1; 'R' => readonly = 1; 'A' => noatime = 1; # mainly useful for flash 'n' => kfsname = arg->earg(); * => arg->usage(); } args = arg->argv(); if(args == nil) arg->usage(); arg = nil; devnone = ref Device(nil, 1); mainlock = Lock.new(); conschan = Chan.new(nil); conschan.msize = Styx->MAXRPC; mode := Sys->ORDWR; if(readonly) mode = Sys->OREAD; wrenfd = sys->open(hd args, mode); if(wrenfd == nil) error(sys->sprint("can't open %s: %r", hd args)); thedevice = ref Device(wrenfd, readonly); if(ream){ if(bufsize <= 0 || bufsize % 512 || bufsize > MAXBUFSIZE) error(sys->sprint("invalid block size %d", bufsize)); RBUFSIZE = bufsize; wrenream(thedevice); }else{ if(!wreninit(thedevice)) error("kfs magic in trouble"); } BUFSIZE = RBUFSIZE - Tagsize; DIRPERBUF = BUFSIZE / Dentrysize; INDPERBUF = BUFSIZE / 4; INDPERBUF2 = INDPERBUF * INDPERBUF; FEPERBUF = (BUFSIZE - Super1size - 4) / 4; emptyblock = array[RBUFSIZE] of {* => byte 0}; iobufinit(30); if(ream){ superream(thedevice, SUPERADDR); rootream(thedevice, ROOTADDR); wstatallow = writeallow = 1; } if(wrencheck(wrenfd)) error("kfs super/root in trouble"); if(!ream && !readonly && !superok(0)){ sys->print("kfs needs check\n"); if(!nocheck) check(thedevice, Cquiet|Cfree); } (d, e) := Dentry.geta(thedevice, ROOTADDR, 0, QPROOT, Bread); if(d != nil && !(d.mode & DDIR)) e = "not a directory"; if(e != nil) error("bad root: "+e); if(debug) d.print(); d.put(); sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); sys->pctl(Sys->NEWFD, wrenfd.fd :: 0 :: 1 :: 2 :: nil); wrenfd = sys->fildes(wrenfd.fd); thedevice.fd = wrenfd; c := chan of int; if(Buffering){ spawn syncproc(c); pid := <-c; if(pid) pids = pid :: pids; } spawn consinit(c); pid := <- c; if(pid) pids = pid :: pids; spawn kfs(sys->fildes(0));} error(s: string){ sys->fprint(sys->fildes(2), "kfs: %s\n", s); for(; pids != nil; pids = tl pids) kill(hd pids); raise "fail:error";} panic(s: string){ sys->fprint(sys->fildes(2), "kfs: panic: %s\n", s); for(; pids != nil; pids = tl pids) kill(hd pids); raise "panic";} syncproc(c: chan of int)Lines 1–500 of 3844. Load more
implement Kfs; # # Copyright © 1991-2003 Lucent Technologies Inc. # Limbo version Copyright © 2004 Vita Nuova Holdings Limited # # # TO DO: # - sync proc; Bmod; process structure # - swiz? include "sys.m"; sys: Sys; Qid, Dir: import Sys; DMEXCL, DMAPPEND, DMDIR: import Sys; QTEXCL, QTAPPEND, QTDIR: import Sys; include "draw.m"; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; NOFID, OEXEC, ORCLOSE, OREAD, OWRITE, ORDWR, OTRUNC: import Styx; IOHDRSZ: import Styx; include "daytime.m"; daytime: Daytime; now: import daytime; include "arg.m"; Kfs: module { init: fn(nil: ref Draw->Context, nil: list of string); }; MAXBUFSIZE: con 16*1024; # # fundamental constants # NAMELEN: con 28; # size of names, including null byte NDBLOCK: con 6; # number of direct blocks in Dentry MAXFILESIZE: con big 16r7FFFFFFF; # Plan 9's limit (kfs's size is signed) SUPERADDR: con 1; ROOTADDR: con 2; QPDIR: con int (1<<31); QPNONE: con 0; QPROOT: con 1; QPSUPER: con 2; # # don't change, these are the mode bits on disc # DALLOC: con 16r8000; DDIR: con 16r4000; DAPND: con 16r2000; DLOCK: con 16r1000; DREAD: con 4; DWRITE: con 2; DEXEC: con 1; # # other constants # MINUTE: con 60; TLOCK: con 5*MINUTE; NTLOCK: con 200; # number of active file locks Buffering: con 1; FID1, FID2, FID3: con 1+iota; None: con 0; # user ID for "none" Noworld: con 9999; # conventional id for "noworld" group Lock: adt { c: chan of int; new: fn(): ref Lock; lock: fn(c: self ref Lock); canlock: fn(c: self ref Lock): int; unlock: fn(c: self ref Lock); }; Dentry: adt { name: string; uid: int; gid: int; muid: int; # not set by plan 9's kfs mode: int; # mode bits on disc: DALLOC etc qid: Qid; # 9p1 format on disc size: big; # only 32-bits on disc, and Plan 9 limits it to signed atime: int; mtime: int; iob: ref Iobuf; # locked block containing directory entry, when in memory buf: array of byte; # pointer into block to packed directory entry, when in memory mod: int; # bits of buf that need updating unpack: fn(a: array of byte): ref Dentry; get: fn(p: ref Iobuf, slot: int): ref Dentry; geta: fn(d: ref Device, addr: int, slot: int, qpath: int, mode: int): (ref Dentry, string); getd: fn(f: ref File, mode: int): (ref Dentry, string); put: fn(d: self ref Dentry); access: fn(d: self ref Dentry, f: int, uid: int); change: fn(d: self ref Dentry, f: int); release: fn(d: self ref Dentry); getblk: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; getblk1: fn(d: self ref Dentry, a: int, tag: int): ref Iobuf; rel2abs: fn(d: self ref Dentry, a: int, tag: int, putb: int): int; trunc: fn(d: self ref Dentry, uid: int); update: fn(d: self ref Dentry); print: fn(d: self ref Dentry); }; Uname, Uids, Umode, Uqid, Usize, Utime: con 1<<iota; # Dentry.mod # # disc structure: # Tag: pad[2] tag[2] path[4] Tagsize: con 2+2+4; Tag: adt { tag: int; path: int; unpack: fn(a: array of byte): Tag; pack: fn(t: self Tag, a: array of byte); }; Superb: adt { iob: ref Iobuf; fstart: int; fsize: int; tfree: int; qidgen: int; # generator for unique ids fsok: int; fbuf: array of byte; # nfree[4] free[FEPERBLK*4]; aliased into containing block get: fn(dev: ref Device, flags: int): ref Superb; touched: fn(s: self ref Superb); put: fn(s: self ref Superb); print: fn(s: self ref Superb); pack: fn(s: self ref Superb, a: array of byte); unpack: fn(a: array of byte): ref Superb; }; Device: adt { fd: ref Sys->FD; ronly: int; # could put locks here if necessary # partitioning by ds(3) }; # # one for each locked qid # Tlock: adt { dev: ref Device; time: int; qpath: int; file: cyclic ref File; # TO DO: probably not needed }; File: adt { qlock: chan of int; qid: Qid; wpath: ref Wpath; tlock: cyclic ref Tlock; # if file is locked fs: ref Device; addr: int; slot: int; lastra: int; # read ahead address fid: int; uid: int; open: int; cons: int; # if opened by console doffset: big; # directory reading dvers: int; dslot: int; new: fn(fid: int): ref File; access: fn(f: self ref File, d: ref Dentry, mode: int): int; lock: fn(f: self ref File); unlock: fn(f: self ref File); }; FREAD, FWRITE, FREMOV, FWSTAT: con 1<<iota; # File.open Chan: adt { fd: ref Sys->FD; # fd request came in on # rlock, wlock: QLock; # lock for reading/writing messages on cp flags: int; flist: list of ref File; # active files fqlock: chan of int; # reflock: RWLock; # lock for Tflush msize: int; # version new: fn(fd: ref Sys->FD): ref Chan; getfid: fn(c: self ref Chan, fid: int, flag: int): ref File; putfid: fn(c: self ref Chan, f: ref File); flock: fn(nil: self ref Chan); funlock: fn(nil: self ref Chan); }; Hiob: adt { link: ref Iobuf; # TO DO: eliminate circular list lk: ref Lock; niob: int; newbuf: fn(h: self ref Hiob): ref Iobuf; }; Iobuf: adt { qlock: chan of int; dev: ref Device; fore: cyclic ref Iobuf; # lru hash chain back: cyclic ref Iobuf; # for lru iobuf: array of byte; # only active while locked xiobuf: array of byte; # "real" buffer pointer addr: int; flags: int; get: fn(dev: ref Device, addr: int, flags: int):ref Iobuf; put: fn(iob: self ref Iobuf); lock: fn(iob: self ref Iobuf); canlock: fn(iob: self ref Iobuf): int; unlock: fn(iob: self ref Iobuf); checktag: fn(iob: self ref Iobuf, tag: int, qpath: int): int; settag: fn(iob: self ref Iobuf, tag: int, qpath: int); }; Wpath: adt { up: cyclic ref Wpath; # pointer upwards in path addr: int; # directory entry addr slot: int; # directory entry slot }; # # error codes generated from the file server # Eaccess: con "access permission denied"; Ealloc: con "phase error -- directory entry not allocated"; Eauth: con "authentication failed"; Eauthmsg: con "kfs: authentication not required"; Ebadspc: con "attach -- bad specifier"; Ebadu: con "attach -- privileged user"; Ebroken: con "close/read/write -- lock is broken"; Echar: con "bad character in directory name"; Econvert: con "protocol botch"; Ecount: con "read/write -- count too big"; Edir1: con "walk -- in a non-directory"; Edir2: con "create -- in a non-directory"; Edot: con "create -- . and .. illegal names"; Eempty: con "remove -- directory not empty"; Eentry: con "directory entry not found"; Eexist: con "create -- file exists"; Efid: con "unknown fid"; Efidinuse: con "fid already in use"; Efull: con "file system full"; Elocked: con "open/create -- file is locked"; Emode: con "open/create -- unknown mode"; Ename: con "create/wstat -- bad character in file name"; Enotd: con "wstat -- attempt to change directory"; Enotg: con "wstat -- not in group"; Enotl: con "wstat -- attempt to change length"; Enotm: con "wstat -- unknown type/mode"; Enotu: con "wstat -- not owner"; Eoffset: con "read/write -- offset negative"; Eopen: con "read/write -- on non open fid"; Ephase: con "phase error -- cannot happen"; Eqid: con "phase error -- qid does not match"; Eqidmode: con "wstat -- qid.qtype/dir.mode mismatch"; Eronly: con "file system read only"; Ersc: con "it's russ's fault. bug him."; Esystem: con "kfs system error"; Etoolong: con "name too long"; Etoobig: con "write -- file size limit"; Ewalk: con "walk -- too many (system wide)"; # # tags on block # Tnone, Tsuper, # the super block Tdir, # directory contents Tind1, # points to blocks Tind2, # points to Tind1 Tfile, # file contents Tfree, # in free list Tbuck, # cache fs bucket Tvirgo, # fake worm virgin bits Tcache, # cw cache things MAXTAG: con iota; # # flags to Iobuf.get # Bread, # read the block if miss Bprobe, # return null if miss Bmod, # set modified bit in buffer Bimm, # set immediate bit in buffer Bres: # never renamed con 1<<iota; # # check flags # Crdall, # read all files Ctag, # rebuild tags Cpfile, # print files Cpdir, # print directories Cfree, # rebuild free list Cream, # clear all bad tags Cbad, # clear all bad blocks Ctouch, # touch old dir and indir Cquiet: # report just nasty things con 1<<iota; # # buffer size variables, determined by RBUFSIZE # RBUFSIZE: int; BUFSIZE: int; DIRPERBUF: int; INDPERBUF: int; INDPERBUF2: int; FEPERBUF: int; emptyblock: array of byte; wrenfd: ref Sys->FD; thedevice: ref Device; devnone: ref Device; wstatallow := 0; writeallow := 0; writegroup := 0; ream := 0; readonly := 0; noatime := 0; localfs: con 1; conschan: ref Chan; consuid := -1; consgid := -1; debug := 0; kfsname: string; consoleout: chan of string; mainlock: ref Lock; pids: list of int; noqid: Qid; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; styx = load Styx Styx->PATH; daytime = load Daytime Daytime->PATH; styx->init(); arg := load Arg Arg->PATH; if(arg == nil) error(sys->sprint("can't load %s: %r", Arg->PATH)); arg->init(args); arg->setusage("disk/kfs [-r [-b bufsize]] [-cADPRW] [-n name] kfsfile"); bufsize := 1024; nocheck := 0; while((o := arg->opt()) != 0) case o { 'c' => nocheck = 1; 'r' => ream = 1; 'b' => bufsize = int arg->earg(); 'D' => debug = !debug; 'P' => writeallow = 1; 'W' => wstatallow = 1; 'R' => readonly = 1; 'A' => noatime = 1; # mainly useful for flash 'n' => kfsname = arg->earg(); * => arg->usage(); } args = arg->argv(); if(args == nil) arg->usage(); arg = nil; devnone = ref Device(nil, 1); mainlock = Lock.new(); conschan = Chan.new(nil); conschan.msize = Styx->MAXRPC; mode := Sys->ORDWR; if(readonly) mode = Sys->OREAD; wrenfd = sys->open(hd args, mode); if(wrenfd == nil) error(sys->sprint("can't open %s: %r", hd args)); thedevice = ref Device(wrenfd, readonly); if(ream){ if(bufsize <= 0 || bufsize % 512 || bufsize > MAXBUFSIZE) error(sys->sprint("invalid block size %d", bufsize)); RBUFSIZE = bufsize; wrenream(thedevice); }else{ if(!wreninit(thedevice)) error("kfs magic in trouble"); } BUFSIZE = RBUFSIZE - Tagsize; DIRPERBUF = BUFSIZE / Dentrysize; INDPERBUF = BUFSIZE / 4; INDPERBUF2 = INDPERBUF * INDPERBUF; FEPERBUF = (BUFSIZE - Super1size - 4) / 4; emptyblock = array[RBUFSIZE] of {* => byte 0}; iobufinit(30); if(ream){ superream(thedevice, SUPERADDR); rootream(thedevice, ROOTADDR); wstatallow = writeallow = 1; } if(wrencheck(wrenfd)) error("kfs super/root in trouble"); if(!ream && !readonly && !superok(0)){ sys->print("kfs needs check\n"); if(!nocheck) check(thedevice, Cquiet|Cfree); } (d, e) := Dentry.geta(thedevice, ROOTADDR, 0, QPROOT, Bread); if(d != nil && !(d.mode & DDIR)) e = "not a directory"; if(e != nil) error("bad root: "+e); if(debug) d.print(); d.put(); sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); sys->pctl(Sys->NEWFD, wrenfd.fd :: 0 :: 1 :: 2 :: nil); wrenfd = sys->fildes(wrenfd.fd); thedevice.fd = wrenfd; c := chan of int; if(Buffering){ spawn syncproc(c); pid := <-c; if(pid) pids = pid :: pids; } spawn consinit(c); pid := <- c; if(pid) pids = pid :: pids; spawn kfs(sys->fildes(0)); } error(s: string) { sys->fprint(sys->fildes(2), "kfs: %s\n", s); for(; pids != nil; pids = tl pids) kill(hd pids); raise "fail:error"; } panic(s: string) { sys->fprint(sys->fildes(2), "kfs: panic: %s\n", s); for(; pids != nil; pids = tl pids) kill(hd pids); raise "panic"; } syncproc(c: chan of int)