diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/dossrv.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/cmd/dossrv.b')
| -rw-r--r-- | appl/cmd/dossrv.b | 3432 |
1 files changed, 3432 insertions, 0 deletions
diff --git a/appl/cmd/dossrv.b b/appl/cmd/dossrv.b new file mode 100644 index 00000000..aefe7948 --- /dev/null +++ b/appl/cmd/dossrv.b @@ -0,0 +1,3432 @@ +implement Dossrv; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "arg.m"; + +include "daytime.m"; + daytime: Daytime; + +include "styx.m"; + styx: Styx; + Tmsg, Rmsg: import styx; + +Dossrv: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); + system: fn(ctxt: ref Draw->Context, args: list of string): string; +}; + +arg0 := "dossrv"; + +deffile: string; +pflag := 0; +debug := 0; + +usage(iscmd: int): string +{ + sys->fprint(sys->fildes(2), "usage: %s [-v] [-s] [-F] [-c] [-S secpertrack] [-f devicefile] [-m mountpoint]\n", arg0); + if(iscmd) + raise "fail:usage"; + return "usage"; +} + +init(nil: ref Draw->Context, args: list of string) +{ + e := init2(nil, args, 1); + if(e != nil){ + sys->fprint(sys->fildes(2), "%s: %s\n", arg0, e); + raise "fail:error"; + } +} + +system(nil: ref Draw->Context, args: list of string): string +{ + e := init2(nil, args, 0); + if(e != nil) + sys->fprint(sys->fildes(2), "%s: %s\n", arg0, e); + return e; +} + +nomod(s: string): string +{ + return sys->sprint("can't load %s: %r", s); +} + +init2(nil: ref Draw->Context, args: list of string, iscmd: int): string +{ + sys = load Sys Sys->PATH; + + pipefd := array[2] of ref Sys->FD; + + srvfile := "/n/dos"; + deffile = ""; # no default, for safety + sectors := 0; + stdin := 0; + + arg := load Arg Arg->PATH; + if(arg == nil) + return nomod(Arg->PATH); + arg->init(args); + arg0 = arg->progname(); + while((o := arg->opt()) != 0) { + case o { + 'v' => + if(debug & STYX_MESS) + debug |= VERBOSE; + debug |= STYX_MESS; + 'F' => + debug |= FAT_INFO; + 'c' => + debug |= CLUSTER_INFO; + iodebug = 1; + 'S' => + s := arg->arg(); + if(s != nil && s[0]>='0' && s[0]<='9') + sectors = int s; + else + return usage(iscmd); + 's' => + stdin = 1; + 'f' => + deffile = arg->arg(); + if(deffile == nil) + return usage(iscmd); + 'm' => + srvfile = arg->arg(); + if(srvfile == nil) + return usage(iscmd); + 'p' => + pflag++; + * => + return usage(iscmd); + } + } + args = arg->argv(); + arg = nil; + + if(deffile == "" || !stdin && srvfile == "") + return usage(iscmd); + + styx = load Styx Styx->PATH; + if(styx == nil) + return nomod(Styx->PATH); + styx->init(); + + daytime = load Daytime Daytime->PATH; + if(daytime == nil) + return nomod(Daytime->PATH); + + iotrackinit(sectors); + + if(!stdin) { + if(sys->pipe(pipefd) < 0) + return sys->sprint("can't create pipe: %r"); + }else{ + pipefd[0] = nil; + pipefd[1] = sys->fildes(1); + } + + dossetup(); + + spawn dossrv(pipefd[1]); + + if(!stdin) { + if(sys->mount(pipefd[0], nil, srvfile, sys->MREPL|sys->MCREATE, deffile) < 0) + return sys->sprint("mount %s: %r", srvfile); + } + + return nil; +} + +# +# Styx server +# + + Enevermind, + Eformat, + Eio, + Enomem, + Enonexist, + Enotdir, + Enofid, + Efidopen, + Efidinuse, + Eexist, + Eperm, + Enofilsys, + Eauth, + Econtig, + Efull, + Eopen, + Ephase: con iota; + +errmsg := array[] of { + Enevermind => "never mind", + Eformat => "unknown format", + Eio => "I/O error", + Enomem => "server out of memory", + Enonexist => "file does not exist", + Enotdir => "not a directory", + Enofid => "no such fid", + Efidopen => "fid already open", + Efidinuse => "fid in use", + Eexist => "file exists", + Eperm => "permission denied", + Enofilsys => "no file system device specified", + Eauth => "authentication failed", + Econtig => "out of contiguous disk space", + Efull => "file system full", + Eopen => "invalid open mode", + Ephase => "phase error -- directory entry not found", +}; + +e(n: int): ref Rmsg.Error +{ + if(n < 0 || n >= len errmsg) + return ref Rmsg.Error(0, "it's thermal problems"); + return ref Rmsg.Error(0, errmsg[n]); +} + +dossrv(rfd: ref Sys->FD) +{ + sys->pctl(Sys->NEWFD, rfd.fd :: 2 :: nil); + rfd = sys->fildes(rfd.fd); + data := array[Styx->MAXRPC] of byte; + while((t := Tmsg.read(rfd, 0)) != nil){ + if(debug & STYX_MESS) + chat(sys->sprint("%s...", t.text())); + + r: ref Rmsg; + pick m := t { + Readerror => + panic(sys->sprint("mount read error: %s", m.error)); + Version => + r = rversion(m); + Auth => + r = rauth(m); + Flush => + r = rflush(m); + Attach => + r = rattach(m); + Walk => + r = rwalk(m); + Open => + r = ropen(m); + Create => + r = rcreate(m); + Read => + r = rread(m); + Write => + r = rwrite(m); + Clunk => + r = rclunk(m); + Remove => + r = rremove(m); + Stat => + r = rstat(m); + Wstat => + r = rwstat(m); + * => + panic("Styx mtype"); + } + pick m := r { + Error => + r.tag = t.tag; + } + rbuf := r.pack(); + if(rbuf == nil) + panic("Rmsg.pack"); + if(debug & STYX_MESS) + chat(sys->sprint("%s\n", r.text())); + if(styx->write(rfd, rbuf, len rbuf) != len rbuf) + panic("mount write"); + } + + if(debug & STYX_MESS) + chat("server EOF\n"); +} + +rversion(t: ref Tmsg.Version): ref Rmsg +{ + (msize, version) := styx->compatible(t, Styx->MAXRPC, Styx->VERSION); + return ref Rmsg.Version(t.tag, msize, version); +} + +rauth(t: ref Tmsg.Auth): ref Rmsg +{ + return ref Rmsg.Error(t.tag, "authentication not required"); +} + +rflush(t: ref Tmsg.Flush): ref Rmsg +{ + return ref Rmsg.Flush(t.tag); +} + +rattach(t: ref Tmsg.Attach): ref Rmsg +{ + root := xfile(t.fid, Clean); + if(root == nil) + return e(Eio); + if(t.aname == nil) + t.aname = deffile; + (xf, ec) := getxfs(t.aname); + root.xf = xf; + if(xf == nil) { + if(root!=nil) + xfile(t.fid, Clunk); + return ref Rmsg.Error(t.tag, ec); + } + if(xf.fmt == 0 && dosfs(xf) < 0){ + if(root!=nil) + xfile(t.fid, Clunk); + return e(Eformat); + } + + root.qid = Sys->Qid(big 0, 0, Sys->QTDIR); + root.xf.rootqid = root.qid; + return ref Rmsg.Attach(t.tag, root.qid); +} + +clone(ofl: ref Xfile, newfid: int): ref Xfile +{ + nfl := xfile(newfid, Clean); + next := nfl.next; + *nfl = *ofl; + nfl.ptr = nil; + nfl.next = next; + nfl.fid = newfid; + refxfs(nfl.xf, 1); + if(ofl.ptr != nil){ + dp := ref *ofl.ptr; + dp.p = nil; + dp.d = nil; + nfl.ptr = dp; + } + return nfl; +} + +walk1(f: ref Xfile, name: string): ref Rmsg.Error +{ + if((f.qid.qtype & Sys->QTDIR) == 0){ + if(debug) + chat(sys->sprint("qid.path=0x%bx...", f.qid.path)); + return e(Enotdir); + } + + if(name == ".") # can't happen + return nil; + + if(name== "..") { + if(f.qid.path == f.xf.rootqid.path) { + if (debug) + chat("walkup from root..."); + return nil; + } + (r,dp) := walkup(f); + if(r < 0) + return e(Enonexist); + + f.ptr = dp; + if(dp.addr == 0) { + f.qid.path = f.xf.rootqid.path; + f.qid.qtype = Sys->QTFILE; + } else { + f.qid.path = QIDPATH(dp); + f.qid.qtype = Sys->QTDIR; + } + } else { + if(getfile(f) < 0) + return e(Enonexist); + (r,dp) := searchdir(f, name, 0,1); + putfile(f); + if(r < 0) + return e(Enonexist); + + f.ptr = dp; + f.qid.path = QIDPATH(dp); + f.qid.qtype = Sys->QTFILE; + if(dp.addr == 0) + f.qid.path = f.xf.rootqid.path; + else { + d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + if((int d.attr & DDIR) != 0) + f.qid.qtype = Sys->QTDIR; + } + putfile(f); + } + return nil; +} + +rwalk(t: ref Tmsg.Walk): ref Rmsg +{ + f := xfile(t.fid, Asis); + if(f==nil) { + if(debug) + chat("no xfile..."); + return e(Enofid); + } + nf: ref Xfile; + if(t.newfid != t.fid) + f = nf = clone(f, t.newfid); + qids: array of Sys->Qid; + if(len t.names > 0){ + savedqid := f.qid; + savedptr := f.ptr; + qids = array[len t.names] of Sys->Qid; + for(i := 0; i < len t.names; i++){ + e := walk1(f, t.names[i]); + if(e != nil){ + f.qid = savedqid; + f.ptr = savedptr; + if(nf != nil) + xfile(t.newfid, Clunk); + if(i == 0) + return e; + return ref Rmsg.Walk(t.tag, qids[0:i]); + } + qids[i] = f.qid; + } + } + return ref Rmsg.Walk(t.tag, qids); +} + +ropen(t: ref Tmsg.Open): ref Rmsg +{ + attr: int; + + omode := 0; + f := xfile(t.fid, Asis); + if(f == nil) + return e(Enofid); + if((f.flags&Omodes) != 0) + return e(Efidopen); + + dp := f.ptr; + if(dp.paddr && (t.mode & Styx->ORCLOSE) != 0) { + # check on parent directory of file to be deleted + p := getsect(f.xf, dp.paddr); + if(p == nil) + return e(Eio); + # 11 is the attr byte offset in a FAT directory entry + attr = int p.iobuf[dp.poffset+11]; + putsect(p); + if((attr & int DRONLY) != 0) + return e(Eperm); + omode |= Orclose; + } else if(t.mode & Styx->ORCLOSE) + omode |= Orclose; + + if(getfile(f) < 0) + return e(Enonexist); + + if(dp.addr != 0) { + d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + attr = int d.attr; + } else + attr = int DDIR; + + case t.mode & 7 { + Styx->OREAD or + Styx->OEXEC => + omode |= Oread; + Styx->ORDWR => + omode |= Oread; + omode |= Owrite; + if(attr & int (DRONLY|DDIR)) { + putfile(f); + return e(Eperm); + } + Styx->OWRITE => + omode |= Owrite; + if(attr & int (DRONLY|DDIR)) { + putfile(f); + return e(Eperm); + } + * => + putfile(f); + return e(Eopen); + } + + if(t.mode & Styx->OTRUNC) { + if((attr & int DDIR)!=0 || (attr & int DRONLY) != 0) { + putfile(f); + return e(Eperm); + } + + if(truncfile(f) < 0) { + putfile(f); + return e(Eio); + } + } + + f.flags |= omode; + putfile(f); + return ref Rmsg.Open(t.tag, f.qid, Styx->MAXFDATA); +} + +mkdentry(xf: ref Xfs, ndp: ref Dosptr, name: string, sname: string, islong: int, nattr: byte, start: array of byte, length: array of byte): int +{ + ndp.p = getsect(xf, ndp.addr); + if(ndp.p == nil) + return Eio; + if(islong && (r := putlongname(xf, ndp, name, sname)) < 0){ + putsect(ndp.p); + if(r == -2) + return Efull; + return Eio; + } + + nd := ref Dosdir(". "," ",byte 0,array[10] of { * => byte 0}, + array[2] of { * => byte 0}, array[2] of { * => byte 0}, + array[2] of { * => byte 0},array[4] of { * => byte 0}); + + nd.attr = nattr; + puttime(nd); + nd.start[0: ] = start[0: 2]; + nd.length[0: ] = length[0: 4]; + + if(islong) + putname(sname[0:8]+"."+sname[8:11], nd); + else + putname(name, nd); + ndp.p.iobuf[ndp.offset: ] = Dosdir.Dd2arr(nd); + ndp.p.flags |= BMOD; + return 0; +} + +rcreate(t: ref Tmsg.Create): ref Rmsg +{ + bp: ref Dosbpb; + omode:=0; + start:=0; + sname := ""; + islong :=0; + + f := xfile(t.fid, Asis); + if(f == nil) + return e(Enofid); + if((f.flags&Omodes) != 0) + return e(Efidopen); + if(getfile(f)<0) + return e(Eio); + + pdp := f.ptr; + if(pdp.addr != 0) + pd := Dosdir.arr2Dd(pdp.p.iobuf[pdp.offset:pdp.offset+DOSDIRSIZE]); + else + pd = nil; + + if(pd != nil) + attr := int pd.attr; + else + attr = DDIR; + + if(!(attr & DDIR) || (attr & DRONLY)) { + putfile(f); + return e(Eperm); + } + + if(t.mode & Styx->ORCLOSE) + omode |= Orclose; + + case (t.mode & 7) { + Styx->OREAD or + Styx->OEXEC => + omode |= Oread; + Styx->OWRITE or + Styx->ORDWR => + if ((t.mode & 7) == Styx->ORDWR) + omode |= Oread; + omode |= Owrite; + if(t.perm & Sys->DMDIR){ + putfile(f); + return e(Eperm); + } + * => + putfile(f); + return e(Eopen); + } + + if(t.name=="." || t.name=="..") { + putfile(f); + return e(Eperm); + } + + (r,ndp) := searchdir(f, t.name, 1, 1); + if(r < 0) { + putfile(f); + if(r == -2) + return e(Efull); + return e(Eexist); + } + + nds := name2de(t.name); + if(nds > 0) { + # long file name, find "new" short name + i := 1; + for(;;) { + sname = long2short(t.name, i); + (r1, tmpdp) := searchdir(f, sname, 0, 0); + if(r1 < 0) + break; + putsect(tmpdp.p); + i++; + } + islong = 1; + } + + # allocate first cluster, if making directory + if(t.perm & Sys->DMDIR) { + bp = f.xf.ptr; + start = falloc(f.xf); + if(start <= 0) { + putfile(f); + return e(Efull); + } + } + + # now we're committed + if(pd != nil) { + puttime(pd); + pdp.p.flags |= BMOD; + } + + f.ptr = ndp; + ndp.p = getsect(f.xf, ndp.addr); + if(ndp.p == nil || + islong && putlongname(f.xf, ndp, t.name, sname) < 0){ + putsect(pdp.p); + if(ndp.p != nil) + putsect(ndp.p); + return e(Eio); + } + + nd := ref Dosdir(". "," ",byte 0,array[10] of { * => byte 0}, + array[2] of { * => byte 0}, array[2] of { * => byte 0}, + array[2] of { * => byte 0},array[4] of { * => byte 0}); + + if((t.perm & 8r222) == 0) + nd.attr |= byte DRONLY; + + puttime(nd); + nd.start[0] = byte start; + nd.start[1] = byte (start>>8); + + if(islong) + putname(sname[0:8]+"."+sname[8:11], nd); + else + putname(t.name, nd); + + f.qid.path = QIDPATH(ndp); + if(t.perm & Sys->DMDIR) { + nd.attr |= byte DDIR; + f.qid.qtype |= Sys->QTDIR; + xp := getsect(f.xf, bp.dataaddr+(start-2)*bp.clustsize); + if(xp == nil) { + if(ndp.p!=nil) + putfile(f); + putsect(pdp.p); + return e(Eio); + } + xd := ref *nd; + xd.name = ". "; + xd.ext = " "; + xp.iobuf[0:] = Dosdir.Dd2arr(xd); + if(pd!=nil) + xd = ref *pd; + else{ + xd = ref Dosdir(".. "," ",byte 0, + array[10] of { * => byte 0}, + array[2] of { * => byte 0}, + array[2] of { * => byte 0}, + array[2] of { * => byte 0}, + array[4] of { * => byte 0}); + + puttime(xd); + xd.attr = byte DDIR; + } + xd.name=".. "; + xd.ext=" "; + xp.iobuf[DOSDIRSIZE:] = Dosdir.Dd2arr(xd); + xp.flags |= BMOD; + putsect(xp); + }else + f.qid.qtype = Sys->QTFILE; + + ndp.p.flags |= BMOD; + tmp := Dosdir.Dd2arr(nd); + ndp.p.iobuf[ndp.offset:]= tmp; + putfile(f); + putsect(pdp.p); + + f.flags |= omode; + return ref Rmsg.Create(t.tag, f.qid, Styx->MAXFDATA); +} + +rread(t: ref Tmsg.Read): ref Rmsg +{ + r: int; + data: array of byte; + + if(((f:=xfile(t.fid, Asis))==nil) || + (f.flags&Oread == 0)) + return e(Eio); + + if((f.qid.qtype & Sys->QTDIR) != 0) { + if(getfile(f) < 0) + return e(Eio); + (r, data) = readdir(f, int t.offset, t.count); + } else { + if(getfile(f) < 0) + return e(Eio); + (r,data) = readfile(f, int t.offset, t.count); + } + putfile(f); + + if(r < 0) + return e(Eio); + return ref Rmsg.Read(t.tag, data[0:r]); +} + +rwrite(t: ref Tmsg.Write): ref Rmsg +{ + if(((f:=xfile(t.fid, Asis))==nil) || + !(f.flags&Owrite)) + return e(Eio); + if(getfile(f) < 0) + return e(Eio); + r := writefile(f, t.data, int t.offset, len t.data); + putfile(f); + if(r < 0){ + if(r == -2) + return e(Efull); + return e(Eio); + } + return ref Rmsg.Write(t.tag, r); +} + +rclunk(t: ref Tmsg.Clunk): ref Rmsg +{ + xfile(t.fid, Clunk); + sync(); + return ref Rmsg.Clunk(t.tag); +} + +doremove(f: ref Xfs, dp: ref Dosptr) +{ + dp.p.iobuf[dp.offset] = byte DOSEMPTY; + dp.p.flags |= BMOD; + for(prevdo := dp.offset-DOSDIRSIZE; prevdo >= 0; prevdo-=DOSDIRSIZE){ + if (dp.p.iobuf[prevdo+11] != byte DLONG) + break; + dp.p.iobuf[prevdo] = byte DOSEMPTY; + } + + if (prevdo <= 0 && dp.prevaddr != -1){ + p := getsect(f,dp.prevaddr); + for(prevdo = f.ptr.sectsize-DOSDIRSIZE; prevdo >= 0; prevdo-=DOSDIRSIZE) { + if(p.iobuf[prevdo+11] != byte DLONG) + break; + p.iobuf[prevdo] = byte DOSEMPTY; + p.flags |= BMOD; + } + putsect(p); + } +} + +rremove(t: ref Tmsg.Remove): ref Rmsg +{ + f := xfile(t.fid, Asis); + if(f == nil) + return e(Enofid); + + if(!f.ptr.addr) { + if(debug) + chat("root..."); + xfile(t.fid, Clunk); + sync(); + return e(Eperm); + } + + # check on parent directory of file to be deleted + parp := getsect(f.xf, f.ptr.paddr); + if(parp == nil) { + xfile(t.fid, Clunk); + sync(); + return e(Eio); + } + + pard := Dosdir.arr2Dd(parp.iobuf[f.ptr.poffset:f.ptr.poffset+DOSDIRSIZE]); + if(f.ptr.paddr && (int pard.attr & DRONLY)) { + if(debug) + chat("parent read-only..."); + putsect(parp); + xfile(t.fid, Clunk); + sync(); + return e(Eperm); + } + + if(getfile(f) < 0){ + if(debug) + chat("getfile failed..."); + putsect(parp); + xfile(t.fid, Clunk); + sync(); + return e(Eio); + } + + dattr := int f.ptr.p.iobuf[f.ptr.offset+11]; + if(dattr & DDIR && emptydir(f) < 0){ + if(debug) + chat("non-empty dir..."); + putfile(f); + putsect(parp); + xfile(t.fid, Clunk); + sync(); + return e(Eperm); + } + if(f.ptr.paddr == 0 && dattr&DRONLY) { + if(debug) + chat("read-only file in root directory..."); + putfile(f); + putsect(parp); + xfile(t.fid, Clunk); + sync(); + return e(Eperm); + } + + doremove(f.xf, f.ptr); + + if(f.ptr.paddr) { + puttime(pard); + parp.flags |= BMOD; + } + + parp.iobuf[f.ptr.poffset:] = Dosdir.Dd2arr(pard); + putsect(parp); + err := 0; + if(truncfile(f) < 0) + err = Eio; + + putfile(f); + xfile(t.fid, Clunk); + sync(); + if(err) + return e(err); + return ref Rmsg.Remove(t.tag); +} + +rstat(t: ref Tmsg.Stat): ref Rmsg +{ + f := xfile(t.fid, Asis); + if(f == nil) + return e(Enofid); + if(getfile(f) < 0) + return e(Eio); + dir := dostat(f); + putfile(f); + return ref Rmsg.Stat(t.tag, *dir); +} + +dostat(f: ref Xfile): ref Sys->Dir +{ + islong :=0; + prevdo: int; + longnamebuf:=""; + + # get file info. + dir := getdir(f.ptr.p.iobuf[f.ptr.offset:f.ptr.offset+DOSDIRSIZE], + f.ptr.addr, f.ptr.offset); + # get previous entry + if(f.ptr.prevaddr == -1) { + # maybe extended, but will never cross sector boundary... + # short filename at beginning of sector.. + if(f.ptr.offset!=0) { + for(prevdo = f.ptr.offset-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE) { + prevdattr := f.ptr.p.iobuf[prevdo+11]; + if(prevdattr != byte DLONG) + break; + islong = 1; + longnamebuf += getnamesect(f.ptr.p.iobuf[prevdo:prevdo+DOSDIRSIZE]); + } + } + } else { + # extended and will cross sector boundary. + for(prevdo = f.ptr.offset-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE) { + prevdattr := f.ptr.p.iobuf[prevdo+11]; + if(prevdattr != byte DLONG) + break; + islong = 1; + longnamebuf += getnamesect(f.ptr.p.iobuf[prevdo:prevdo+DOSDIRSIZE]); + } + if (prevdo < 0) { + p := getsect(f.xf,f.ptr.prevaddr); + for(prevdo = f.xf.ptr.sectsize-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE){ + prevdattr := p.iobuf[prevdo+11]; + if(prevdattr != byte DLONG) + break; + islong = 1; + longnamebuf += getnamesect(p.iobuf[prevdo:prevdo+DOSDIRSIZE]); + } + putsect(p); + } + } + if(islong) + dir.name = longnamebuf; + return dir; +} + +nameok(elem: string): int +{ + isfrog := array[256] of { + # NUL + 1, 1, 1, 1, 1, 1, 1, 1, + # BKS + 1, 1, 1, 1, 1, 1, 1, 1, + # DLE + 1, 1, 1, 1, 1, 1, 1, 1, + # CAN + 1, 1, 1, 1, 1, 1, 1, 1, +# ' ' => 1, + '/' => 1, 16r7f => 1, * => 0 + }; + + for(i:=0; i < len elem; i++) { + if(isfrog[elem[i]]) + return -1; + } + return 0; +} + +rwstat(t: ref Tmsg.Wstat): ref Rmsg +{ + f := xfile(t.fid, Asis); + if(f == nil) + return e(Enofid); + + if(getfile(f) < 0) + return e(Eio); + + dp := f.ptr; + + if(dp.addr == 0){ # root + putfile(f); + return e(Eperm); + } + + changes := 0; + dir := dostat(f); + wdir := ref t.stat; + + if(dir.uid != wdir.uid || dir.gid != wdir.gid){ + putfile(f); + return e(Eperm); + } + + if(dir.mtime != wdir.mtime || ((dir.mode^wdir.mode) & 8r777)) + changes = 1; + + if((wdir.mode & 7) != ((wdir.mode >> 3) & 7) + || (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){ + putfile(f); + return e(Eperm); + } + + if(dir.name != wdir.name){ + # temporarily disable this + # g.errno = Eperm; + # putfile(f); + # return; + + # + # grab parent directory of file to be changed and check for write perm + # rename also disallowed for read-only files in root directory + # + parp := getsect(f.xf, dp.paddr); + if(parp == nil){ + putfile(f); + return e(Eio); + } + # pard := Dosdir.arr2Dd(parp.iobuf[dp.poffset: dp.poffset+DOSDIRSIZE]); + pardattr := int parp.iobuf[dp.poffset+11]; + dpd := Dosdir.arr2Dd(dp.p.iobuf[dp.offset: dp.offset+DOSDIRSIZE]); + if(dp.paddr != 0 && int pardattr & DRONLY + || dp.paddr == 0 && int dpd.attr & DRONLY){ + putsect(parp); + putfile(f); + return e(Eperm); + } + + # + # retrieve info from old entry + # + oaddr := dp.addr; + ooffset := dp.offset; + d := dpd; + od := *d; + # start := getstart(f.xf, d); + start := d.start; + length := d.length; + attr := d.attr; + + # + # temporarily release file to allow other directory ops: + # walk to parent, validate new name + # then remove old entry + # + putfile(f); + pf := ref *f; + pdp := ref Dosptr(dp.paddr, dp.poffset, 0, 0, 0, 0, -1, -1, parp, nil); + # if(pdp.addr != 0) + # pdpd := Dosdir.arr2Dd(parp.iobuf[pdp.offset: pdp.offset+DOSDIRSIZE]); + # else + # pdpd = nil; + pf.ptr = pdp; + if(wdir.name == "." || wdir.name == ".."){ + putsect(parp); + return e(Eperm); + } + islong := 0; + sname := ""; + nds := name2de(wdir.name); + if(nds > 0) { + # long file name, find "new" short name + i := 1; + for(;;) { + sname = long2short(wdir.name, i); + (r1, tmpdp) := searchdir(f, sname, 0, 0); + if(r1 < 0) + break; + putsect(tmpdp.p); + i++; + } + islong = 1; + }else{ + (b, e) := dosname(wdir.name); + sname = b+e; + } + # (r, ndp) := searchdir(pf, wdir.name, 1, 1); + # if(r < 0){ + # putsect(parp); + # g.errno = Eperm; + # return; + # } + if(getfile(f) < 0){ + putsect(parp); + return e(Eio); + } + doremove(f.xf, dp); + putfile(f); + + # + # search for dir entry again, since we may be able to use the old slot, + # and we need to set up the naddr field if a long name spans the block. + # create new entry. + # + r := 0; + (r, dp) = searchdir(pf, sname, 1, islong); + if(r < 0){ + putsect(parp); + return e(Ephase); + } + if((r = mkdentry(pf.xf, dp, wdir.name, sname, islong, attr, start, length)) != 0){ + putsect(parp); + return e(r); + } + putsect(parp); + + # + # relocate up other fids to the same file, if it moved + # + f.ptr = dp; + f.qid.path = QIDPATH(dp); + if(oaddr != dp.addr || ooffset != dp.offset) + dosptrreloc(f, dp, oaddr, ooffset); + changes = 1; + # f = nil; + } + + if(changes){ + d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + putdir(d, wdir); + dp.p.iobuf[dp.offset: ] = Dosdir.Dd2arr(d); + dp.p.flags |= BMOD; + } + if(f != nil) + putfile(f); + sync(); + return ref Rmsg.Wstat(t.tag); +} + +# +# FAT file system format +# + +Dospart: adt { + active: byte; + hstart: byte; + cylstart: array of byte; + typ: byte; + hend: byte; + cylend: array of byte; + start: array of byte; + length: array of byte; +}; + +Dosboot: adt { + arr2Db: fn(arr: array of byte): ref Dosboot; + magic: array of byte; + version: array of byte; + sectsize: array of byte; + clustsize: byte; + nresrv: array of byte; + nfats: byte; + rootsize: array of byte; + volsize: array of byte; + mediadesc: byte; + fatsize: array of byte; + trksize: array of byte; + nheads: array of byte; + nhidden: array of byte; + bigvolsize: array of byte; + driveno: byte; + bootsig: byte; + volid: array of byte; + label: array of byte; +}; + +Dosbpb: adt { + sectsize: int; # in bytes + clustsize: int; # in sectors + nresrv: int; # sectors + nfats: int; # usually 2 + rootsize: int; # number of entries + volsize: int; # in sectors + mediadesc: int; + fatsize: int; # in sectors + fatclusters: int; + fatbits: int; # 12 or 16 + fataddr: int; #big; # sector number + rootaddr: int; #big; + dataaddr: int; #big; + freeptr: int; #big; # next free cluster candidate +}; + +Dosdir: adt { + Dd2arr: fn(d: ref Dosdir): array of byte; + arr2Dd: fn(arr: array of byte): ref Dosdir; + name: string; + ext: string; + attr: byte; + reserved: array of byte; + time: array of byte; + date: array of byte; + start: array of byte; + length: array of byte; +}; + +Dosptr: adt { + addr: int; # of file's directory entry + offset: int; + paddr: int; # of parent's directory entry + poffset: int; + iclust: int; # ordinal within file + clust: int; + prevaddr: int; + naddr: int; + p: ref Iosect; + d: ref Dosdir; +}; + +Asis, Clean, Clunk: con iota; + +FAT12: con byte 16r01; +FAT16: con byte 16r04; +FATHUGE: con byte 16r06; +DMDDO: con 16r54; +DRONLY: con 16r01; +DHIDDEN: con 16r02; +DSYSTEM: con 16r04; +DVLABEL: con 16r08; +DDIR: con 16r10; +DARCH: con 16r20; +DLONG: con DRONLY | DHIDDEN | DSYSTEM | DVLABEL; +DMLONG: con DLONG | DDIR | DARCH; + +DOSDIRSIZE: con 32; +DOSEMPTY: con 16rE5; +DOSRUNES: con 13; + +FATRESRV: con 2; + +Oread: con 1; +Owrite: con 2; +Orclose: con 4; +Omodes: con 3; + +VERBOSE, STYX_MESS, FAT_INFO, CLUSTER_INFO: con (1 << iota); + +nowt, nowt1: int; +tzoff: int; + +# +# because we map all incoming short names from all upper to all lower case, +# and FAT cannot store mixed case names in short name form, +# we'll declare upper case as unacceptable to decide whether a long name +# is needed on output. thus, long names are always written in the case +# in the system call, and are always read back as written; short names +# are produced by the common case of writing all lower case letters +# +isdos := array[256] of { + 'a' to 'z' => 1, 'A' to 'Z' => 0, '0' to '9' => 1, + ' ' => 1, '$' => 1, '%' => 1, '"' => 1, '-' => 1, '_' => 1, '@' => 1, + '~' => 1, '`' => 1, '!' => 1, '(' => 1, ')' => 1, '{' => 1, '}' => 1, '^' => 1, + '#' => 1, '&' => 1, + * => 0 +}; + +dossetup() +{ + nowt = daytime->now(); + nowt1 = sys->millisec(); + tzoff = daytime->local(0).tzoff; +} + +# make xf into a Dos file system... or die trying to. +dosfs(xf: ref Xfs): int +{ + mbroffset := 0; + i: int; + p: ref Iosect; + +Dmddo: + for(;;) { + for(i=2; i>0; i--) { + p = getsect(xf, 0); + if(p == nil) + return -1; + + if((mbroffset == 0) && (p.iobuf[0] == byte 16re9)) + break; + + # Check if the jump displacement (magic[1]) is too + # short for a FAT. DOS 4.0 MBR has a displacement of 8. + if(p.iobuf[0] == byte 16reb && + p.iobuf[2] == byte 16r90 && + p.iobuf[1] != byte 16r08) + break; + + if(i < 2 || + p.iobuf[16r1fe] != byte 16r55 || + p.iobuf[16r1ff] != byte 16raa) { + i = 0; + break; + } + + dp := 16r1be; + for(j:=4; j>0; j--) { + if(debug) { + chat(sys->sprint("16r%2.2ux (%d,%d) 16r%2.2ux (%d,%d) %d %d...", + int p.iobuf[dp], int p.iobuf[dp+1], + bytes2short(p.iobuf[dp+2: dp+4]), + int p.iobuf[dp+4], int p.iobuf[dp+5], + bytes2short(p.iobuf[dp+6: dp+8]), + bytes2int(p.iobuf[dp+8: dp+12]), + bytes2int(p.iobuf[dp+12:dp+16]))); + } + + # Check for a disc-manager partition in the MBR. + # Real MBR is at lba 63. Unfortunately it starts + # with 16rE9, hence the check above against magic. + if(int p.iobuf[dp+4] == DMDDO) { + mbroffset = 63*Sectorsize; + putsect(p); + purgebuf(xf); + xf.offset += mbroffset; + break Dmddo; + } + + # Make sure it really is the right type, other + # filesystems can look like a FAT + # (e.g. OS/2 BOOT MANAGER). + if(p.iobuf[dp+4] == FAT12 || + p.iobuf[dp+4] == FAT16 || + p.iobuf[dp+4] == FATHUGE) + break; + dp+=16; + } + + if(j <= 0) { + if(debug) + chat("no active partition..."); + putsect(p); + return -1; + } + + offset := bytes2int(p.iobuf[dp+8:dp+12])* Sectorsize; + putsect(p); + purgebuf(xf); + xf.offset = mbroffset+offset; + } + break; + } + if(i <= 0) { + if(debug) + chat("bad magic..."); + putsect(p); + return -1; + } + + b := Dosboot.arr2Db(p.iobuf); + if(debug & FAT_INFO) + bootdump(b); + + bp := ref Dosbpb; + xf.ptr = bp; + xf.fmt = 1; + + bp.sectsize = bytes2short(b.sectsize); + bp.clustsize = int b.clustsize; + bp.nresrv = bytes2short(b.nresrv); + bp.nfats = int b.nfats; + bp.rootsize = bytes2short(b.rootsize); + bp.volsize = bytes2short(b.volsize); + if(bp.volsize == 0) + bp.volsize = bytes2int(b.bigvolsize); + bp.mediadesc = int b.mediadesc; + bp.fatsize = bytes2short(b.fatsize); + + bp.fataddr = int bp.nresrv; + bp.rootaddr = bp.fataddr + bp.nfats*bp.fatsize; + i = bp.rootsize*DOSDIRSIZE + bp.sectsize-1; + i /= bp.sectsize; + bp.dataaddr = bp.rootaddr + i; + bp.fatclusters = FATRESRV+(bp.volsize - bp.dataaddr)/bp.clustsize; + if(bp.fatclusters < 4087) + bp.fatbits = 12; + else + bp.fatbits = 16; + bp.freeptr = 2; + if(debug & FAT_INFO){ + chat(sys->sprint("fatbits=%d (%d clusters)...", + bp.fatbits, bp.fatclusters)); + for(i=0; i< int b.nfats; i++) + chat(sys->sprint("fat %d: %d...", + i, bp.fataddr+i*bp.fatsize)); + chat(sys->sprint("root: %d...", bp.rootaddr)); + chat(sys->sprint("data: %d...", bp.dataaddr)); + } + putsect(p); + return 0; +} + +QIDPATH(dp: ref Dosptr): big +{ + return big (dp.addr*(Sectorsize/DOSDIRSIZE) + dp.offset/DOSDIRSIZE); +} + +isroot(addr: int): int +{ + return addr == 0; +} + +getfile(f: ref Xfile): int +{ + dp := f.ptr; + if(dp.p!=nil) + panic("getfile"); + if(dp.addr < 0) + panic("getfile address"); + p := getsect(f.xf, dp.addr); + if(p == nil) + return -1; + + dp.d = nil; + if(!isroot(dp.addr)) { + if(f.qid.path != QIDPATH(dp)){ + if(debug) { + chat(sys->sprint("qid mismatch f=0x%x d=0x%x...", + int f.qid.path, int QIDPATH(dp))); + } + putsect(p); + return -1; + } + # dp.d = Dosdir.arr2Dd(p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + } + dp.p = p; + return 0; +} + +putfile(f: ref Xfile) +{ + dp := f.ptr; + if(dp.p==nil) + panic("putfile"); + putsect(dp.p); + dp.p = nil; + dp.d = nil; +} + +getstart(nil: ref Xfs, d: ref Dosdir): int +{ + start := bytes2short(d.start); +# if(xf.isfat32) +# start |= bytes2short(d.hstart)<<16; + return start; +} + +putstart(nil: ref Xfs, d: ref Dosdir, start: int) +{ + d.start[0] = byte start; + d.start[1] = byte (start>>8); +# if(xf.isfat32){ +# d.hstart[0] = start>>16; +# d.hstart[1] = start>>24; +# } +} + +# +# return the disk cluster for the iclust cluster in f +# +fileclust(f: ref Xfile, iclust: int, cflag: int): int +{ + + bp := f.xf.ptr; + dp := f.ptr; + if(isroot(dp.addr)) + return -1; # root directory for old FAT format does not start on a cluster boundary + d := dp.d; + if(d == nil){ + if(dp.p == nil) + panic("fileclust"); + d = Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + } + next := 0; + start := getstart(f.xf, d); + if(start == 0) { + if(!cflag) + return -1; + start = falloc(f.xf); + if(start <= 0) + return -1; + puttime(d); + putstart(f.xf, d, start); + dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); + dp.p.flags |= BMOD; + dp.clust = 0; + } + + clust, nskip: int; + if(dp.clust == 0 || iclust < dp.iclust) { + clust = start; + nskip = iclust; + } else { + clust = dp.clust; + nskip = iclust - dp.iclust; + } + + if(debug & CLUSTER_INFO && nskip > 0) + chat(sys->sprint("clust %d, skip %d...", clust, nskip)); + + if(clust <= 0) + return -1; + + if(nskip > 0) { + while(--nskip >= 0) { + next = getfat(f.xf, clust); + if(debug & CLUSTER_INFO) + chat(sys->sprint(".%d", next)); + if(next <= 0){ + if(!cflag) + break; + next = falloc(f.xf); + if(next <= 0) + return -1; + putfat(f.xf, clust, next); + } + clust = next; + } + if(next <= 0) + return -1; + dp.clust = clust; + dp.iclust = iclust; + } + if(debug & CLUSTER_INFO) + chat(sys->sprint(" clust(%d)=0x%x...", iclust, clust)); + return clust; +} + +# +# return the disk sector for the isect disk sector in f, +# allocating space if necessary and cflag is set +# +fileaddr(f: ref Xfile, isect: int, cflag: int): int +{ + bp := f.xf.ptr; + dp := f.ptr; + if(isroot(dp.addr)) { + if(isect*bp.sectsize >= bp.rootsize*DOSDIRSIZE) + return -1; + return bp.rootaddr + isect; + } + clust := fileclust(f, isect/bp.clustsize, cflag); + if(clust < 0) + return -1; + return clust2sect(bp, clust) + isect%bp.clustsize; +} + +# +# look for a directory entry matching name +# always searches for long names which match a short name +# +# if creating (cflag is set), set address of available slot and allocate next cluster if necessary +# +searchdir(f: ref Xfile, name: string, cflag: int, lflag: int): (int, ref Dosptr) +{ + xf := f.xf; + bp := xf.ptr; + addr1 := -1; + addr2 := -1; + prevaddr1 := -1; + o1 := 0; + dp := ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); # prevaddr and naddr are -1 + dp.paddr = f.ptr.addr; + dp.poffset = f.ptr.offset; + islong :=0; + buf := ""; + + need := 1; + if(lflag && cflag) + need += name2de(name); + if(!lflag) { + name = name[0:8]+"."+name[8:11]; + i := len name -1; + while(i >= 0 && (name[i]==' ' || name[i] == '.')) + i--; + name = name[0:i+1]; + } + + addr := -1; + prevaddr: int; + have := 0; + for(isect:=0;; isect++) { + prevaddr = addr; + addr = fileaddr(f, isect, cflag); + if(addr < 0) + break; + p := getsect(xf, addr); + if(p == nil) + break; + for(o:=0; o<bp.sectsize; o+=DOSDIRSIZE) { + dattr := int p.iobuf[o+11]; + dname0 := p.iobuf[o]; + if(dname0 == byte 16r00) { + if(debug) + chat("end dir(0)..."); + putsect(p); + if(!cflag) + return (-1, nil); + + # + # addr1 and o1 are the start of the dirs + # addr2 is the optional second cluster used if the long name + # entry does not fit within the addr1 cluster + # have tells us the number of contiguous free dirs + # starting at addr1.o1; need is the number needed to hold the long name + # + if(addr1 < 0){ + addr1 = addr; + prevaddr1 = prevaddr; + o1 = o; + } + nleft := (bp.sectsize-o)/DOSDIRSIZE; + if(addr2 < 0 && nleft+have < need){ + addr2 = fileaddr(f, isect+1, cflag); + if(addr2 < 0){ + if(debug) + chat("end dir(2)..."); + return (-2, nil); + } + }else if(addr2 < 0) + addr2 = addr; + if(addr2 == addr1) + addr2 = -1; + if(debug) + chat(sys->sprint("allocate addr1=%d,%d addr2=%d for %s nleft=%d have=%d need=%d", addr1, o1, addr2, name, nleft, have, need)); + dp.addr = addr1; + dp.offset = o1; + dp.prevaddr = prevaddr1; + dp.naddr = addr2; + return (0, dp); + } + + if(dname0 == byte DOSEMPTY) { + if(debug) + chat("empty..."); + have++; + if(addr1 == -1){ + addr1 = addr; + o1 = o; + prevaddr1 = prevaddr; + } + if(addr2 == -1 && have >= need) + addr2 = addr; + continue; + } + have = 0; + if(addr2 == -1) + addr1 = -1; + + if(0 && lflag && debug) + dirdump(p.iobuf[o:o+DOSDIRSIZE],addr,o); + + if((dattr & DMLONG) == DLONG) { + if(!islong) + buf = ""; + islong = 1; + buf = getnamesect(p.iobuf[o:o+DOSDIRSIZE]) + buf; # getnamesect should return sum + continue; + } + if(dattr & DVLABEL) { + islong = 0; + continue; + } + + if(!islong || !lflag) + buf = getname(p.iobuf[o:o+DOSDIRSIZE]); + islong = 0; + + if(debug) + chat(sys->sprint("cmp: [%s] [%s]", buf, name)); + if(mystrcmp(buf, name) != 0) { + buf=""; + continue; + } + if(debug) + chat("found\n"); + + if(cflag) { + putsect(p); + return (-1,nil); + } + + dp.addr = addr; + dp.prevaddr = prevaddr; + dp.offset = o; + dp.p = p; + #dp.d = Dosdir.arr2Dd(p.iobuf[o:o+DOSDIRSIZE]); + return (0, dp); + } + putsect(p); + } + if(debug) + chat("end dir(1)..."); + if(!cflag) + return (-1, nil); + # + # end of root directory or end of non-root directory on cluster boundary + # + if(addr1 < 0){ + addr1 = fileaddr(f, isect, 1); + if(addr1 < 0) + return (-2, nil); + prevaddr1 = prevaddr; + o1 = 0; + }else{ + if(addr2 < 0 && have < need){ + addr2 = fileaddr(f, isect, 1); + if(addr2 < 0) + return (-2, nil); + } + } + if(addr2 == addr1) + addr2 = -1; + dp.addr = addr1; + dp.offset = o1; + dp.prevaddr = prevaddr1; + dp.naddr = addr2; + return (0, dp); +} + +emptydir(f: ref Xfile): int +{ + for(isect:=0;; isect++) { + addr := fileaddr(f, isect, 0); + if(addr < 0) + break; + + p := getsect(f.xf, addr); + if(p == nil) + return -1; + + for(o:=0; o<f.xf.ptr.sectsize; o+=DOSDIRSIZE) { + dname0 := p.iobuf[o]; + dattr := int p.iobuf[o+11]; + + if(dname0 == byte 16r00) { + putsect(p); + return 0; + } + + if(dname0 == byte DOSEMPTY || dname0 == byte '.') + continue; + + if(dattr & DVLABEL) + continue; # ignore any long name entries: it's empty if there are no short ones + + putsect(p); + return -1; + } + putsect(p); + } + return 0; +} + +readdir(f:ref Xfile, offset: int, count: int): (int, array of byte) +{ + xf := f.xf; + bp := xf.ptr; + rcnt := 0; + buf := array[Styx->MAXFDATA] of byte; + islong :=0; + longnamebuf:=""; + + if(count <= 0) + return (0, nil); + +Read: + for(isect:=0;; isect++) { + addr := fileaddr(f, isect, 0); + if(addr < 0) + break; + p := getsect(xf, addr); + if(p == nil) + return (-1,nil); + + for(o:=0; o<bp.sectsize; o+=DOSDIRSIZE) { + dname0 := int p.iobuf[o]; + dattr := int p.iobuf[o+11]; + + if(dname0 == 16r00) { + putsect(p); + break Read; + } + + if(dname0 == DOSEMPTY) + continue; + + if(dname0 == '.') { + dname1 := int p.iobuf[o+1]; + if(dname1 == ' ' || dname1 == 0) + continue; + dname2 := int p.iobuf[o+2]; + if(dname1 == '.' && + (dname2 == ' ' || dname2 == 0)) + continue; + } + + if((dattr & DMLONG) == DLONG) { + if(!islong) + longnamebuf = ""; + longnamebuf = getnamesect(p.iobuf[o:o+DOSDIRSIZE]) + longnamebuf; + islong = 1; + continue; + } + if(dattr & DVLABEL) { + islong = 0; + continue; + } + + dir := getdir(p.iobuf[o:o+DOSDIRSIZE], addr, o); + if(islong) { + dir.name = longnamebuf; + longnamebuf = ""; + islong = 0; + } + d := styx->packdir(*dir); + if(offset > 0) { + offset -= len d; + islong = 0; + continue; + } + if(rcnt+len d > count){ + putsect(p); + break Read; + } + buf[rcnt:] = d; + rcnt += len d; + if(rcnt >= count) { + putsect(p); + break Read; + } + } + putsect(p); + } + + return (rcnt, buf[0:rcnt]); +} + +walkup(f: ref Xfile): (int, ref Dosptr) +{ + bp := f.xf.ptr; + dp := f.ptr; + o: int; + ndp:= ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); + ndp.addr = dp.paddr; + ndp.offset = dp.poffset; + + if(debug) + chat(sys->sprint("walkup: paddr=0x%x...", dp.paddr)); + + if(dp.paddr == 0) + return (0,ndp); + + p := getsect(f.xf, dp.paddr); + if(p == nil) + return (-1,nil); + + if(debug) + dirdump(p.iobuf[dp.poffset:dp.poffset+DOSDIRSIZE],dp.paddr,dp.poffset); + + xd := Dosdir.arr2Dd(p.iobuf[dp.poffset:dp.poffset+DOSDIRSIZE]); + start := getstart(f.xf, xd); + if(debug & CLUSTER_INFO) + if(debug) + chat(sys->sprint("start=0x%x...", start)); + putsect(p); + if(start == 0) + return (-1,nil); + + # + # check that parent's . points to itself + # + p = getsect(f.xf, bp.dataaddr + (start-2)*bp.clustsize); + if(p == nil) + return (-1,nil); + + if(debug) + dirdump(p.iobuf,0,0); + + xd = Dosdir.arr2Dd(p.iobuf); + if(p.iobuf[0]!= byte '.' || + p.iobuf[1]!= byte ' ' || + start != getstart(f.xf, xd)) { + if(p!=nil) + putsect(p); + return (-1,nil); + } + + if(debug) + dirdump(p.iobuf[DOSDIRSIZE:],0,0); + + # + # parent's .. is the next entry, and has start of parent's parent + # + xd = Dosdir.arr2Dd(p.iobuf[DOSDIRSIZE:]); + if(p.iobuf[32] != byte '.' || p.iobuf[33] != byte '.') { + if(p != nil) + putsect(p); + return (-1,nil); + } + + # + # we're done if parent is root + # + pstart := getstart(f.xf, xd); + putsect(p); + if(pstart == 0) + return (0, ndp); + + # + # check that parent's . points to itself + # + p = getsect(f.xf, clust2sect(bp, pstart)); + if(p == nil) { + if(debug) + chat(sys->sprint("getsect %d failed\n", pstart)); + return (-1,nil); + } + if(debug) + dirdump(p.iobuf,0,0); + xd = Dosdir.arr2Dd(p.iobuf); + if(p.iobuf[0]!= byte '.' || + p.iobuf[1]!=byte ' ' || + pstart!=getstart(f.xf, xd)) { + if(p != nil) + putsect(p); + return (-1,nil); + } + + # + # parent's parent's .. is the next entry, and has start of parent's parent's parent + # + if(debug) + dirdump(p.iobuf[DOSDIRSIZE:],0,0); + + xd = Dosdir.arr2Dd(p.iobuf[DOSDIRSIZE:]); + if(xd.name[0] != '.' || xd.name[1] != '.') { + if(p != nil) + putsect(p); + return (-1,nil); + } + ppstart :=getstart(f.xf, xd); + putsect(p); + + # + # open parent's parent's parent, and walk through it until parent's paretn is found + # need this to find parent's parent's addr and offset + # + ppclust := ppstart; + # TO DO: FAT32 + if(ppclust != 0) + k := clust2sect(bp, ppclust); + else + k = bp.rootaddr; + p = getsect(f.xf, k); + if(p == nil) { + if(debug) + chat(sys->sprint("getsect %d failed\n", k)); + return (-1,nil); + } + + if(debug) + dirdump(p.iobuf,0,0); + + if(ppstart) { + xd = Dosdir.arr2Dd(p.iobuf); + if(p.iobuf[0]!= byte '.' || + p.iobuf[1]!= byte ' ' || + ppstart!=getstart(f.xf, xd)) { + if(p!=nil) + putsect(p); + return (-1,nil); + } + } + + for(so:=1; ;so++) { + for(o=0; o<bp.sectsize; o+=DOSDIRSIZE) { + xdname0 := p.iobuf[o]; + if(xdname0 == byte 16r00) { + if(debug) + chat("end dir\n"); + if(p != nil) + putsect(p); + return (-1,nil); + } + + if(xdname0 == byte DOSEMPTY) + continue; + + #xd = Dosdir.arr2Dd(p.iobuf[o:o+DOSDIRSIZE]); + xdstart:= p.iobuf[o+26:o+28]; # TO DO: getstart + if(bytes2short(xdstart) == pstart) { + putsect(p); + ndp.paddr = k; + ndp.poffset = o; + return (0,ndp); + } + } + if(ppclust) { + if(so%bp.clustsize == 0) { + ppstart = getfat(f.xf, ppstart); + if(ppstart < 0){ + if(debug) + chat(sys->sprint("getfat %d fail\n", + ppstart)); + if(p != nil) + putsect(p); + return (-1,nil); + } + } + k = clust2sect(bp, ppclust) + + so%bp.clustsize; + } + else { + if(so*bp.sectsize >= bp.rootsize*DOSDIRSIZE) { + if(p != nil) + putsect(p); + return (-1,nil); + } + k = bp.rootaddr + so; + } + putsect(p); + p = getsect(f.xf, k); + if(p == nil) { + if(debug) + chat(sys->sprint("getsect %d failed\n", k)); + return (-1,nil); + } + } + putsect(p); + ndp.paddr = k; + ndp.poffset = o; + return (0,ndp); +} + +readfile(f: ref Xfile, offset: int, count: int): (int, array of byte) +{ + xf := f.xf; + bp := xf.ptr; + dp := f.ptr; + + length := bytes2int(dp.p.iobuf[dp.offset+28:dp.offset+32]); + rcnt := 0; + if(offset >= length) + return (0,nil); + buf := array[Styx->MAXFDATA] of byte; + if(offset+count >= length) + count = length - offset; + isect := offset/bp.sectsize; + o := offset%bp.sectsize; + while(count > 0) { + addr := fileaddr(f, isect++, 0); + if(addr < 0) + break; + c := bp.sectsize - o; + if(c > count) + c = count; + p := getsect(xf, addr); + if(p == nil) + return (-1, nil); + buf[rcnt:] = p.iobuf[o:o+c]; + putsect(p); + count -= c; + rcnt += c; + o = 0; + } + return (rcnt, buf[0:rcnt]); +} + +writefile(f: ref Xfile, buf: array of byte, offset,count: int): int +{ + xf := f.xf; + bp := xf.ptr; + dp := f.ptr; + addr := 0; + c: int; + rcnt := 0; + p: ref Iosect; + + d := dp.d; + if(d == nil) + d = Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + isect := offset/bp.sectsize; + + o := offset%bp.sectsize; + while(count > 0) { + addr = fileaddr(f, isect++, 1); + if(addr < 0) + break; + c = bp.sectsize - o; + if(c > count) + c = count; + if(c == bp.sectsize){ + p = getosect(xf, addr); + if(p == nil) + return -1; + p.flags = 0; + }else{ + p = getsect(xf, addr); + if(p == nil) + return -1; + } + p.iobuf[o:] = buf[rcnt:rcnt+c]; + p.flags |= BMOD; + putsect(p); + count -= c; + rcnt += c; + o = 0; + } + if(rcnt <= 0 && addr < 0) + return -2; + length := 0; + dlen := bytes2int(d.length); + if(rcnt > 0) + length = offset+rcnt; + else if(dp.addr && dp.clust) { + c = bp.clustsize*bp.sectsize; + if(dp.iclust > (dlen+c-1)/c) + length = c*dp.iclust; + } + if(length > dlen) { + d.length[0] = byte length; + d.length[1] = byte (length>>8); + d.length[2] = byte (length>>16); + d.length[3] = byte (length>>24); + } + puttime(d); + dp.p.flags |= BMOD; + dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); + return rcnt; +} + +truncfile(f: ref Xfile): int +{ + xf := f.xf; + bp := xf.ptr; + dp := f.ptr; + d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); + + clust := getstart(f.xf, d); + putstart(f.xf, d, 0); + while(clust > 0) { + next := getfat(xf, clust); + putfat(xf, clust, 0); + clust = next; + } + + d.length[0] = byte 0; + d.length[1] = byte 0; + d.length[2] = byte 0; + d.length[3] = byte 0; + + dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); + dp.iclust = 0; + dp.clust = 0; + dp.p.flags |= BMOD; + + return 0; +} + +getdir(arr: array of byte, addr,offset: int) :ref Sys->Dir +{ + dp := ref Sys->Dir; + + if(arr == nil || addr == 0) { + dp.name = ""; + dp.qid.path = big 0; + dp.qid.qtype = Sys->QTDIR; + dp.length = big 0; + dp.mode = Sys->DMDIR|8r777; + } + else { + dp.name = getname(arr); + for(i:=0; i < len dp.name; i++) + if(dp.name[i]>='A' && dp.name[i]<='Z') + dp.name[i] = dp.name[i]-'A'+'a'; + + # dp.qid.path = bytes2short(d.start); + dp.qid.path = big (addr*(Sectorsize/DOSDIRSIZE) + offset/DOSDIRSIZE); + dattr := int arr[11]; + + if(dattr & DRONLY) + dp.mode = 8r444; + else + dp.mode = 8r666; + + dp.atime = gtime(arr); + dp.mtime = dp.atime; + if(dattr & DDIR) { + dp.length = big 0; + dp.qid.qtype |= Styx->QTDIR; + dp.mode |= Sys->DMDIR|8r111; + } + else + dp.length = big bytes2int(arr[28:32]); + + if(dattr & DSYSTEM){ + dp.mode |= Styx->DMEXCL; + dp.qid.qtype |= Styx->QTEXCL; + } + } + + dp.qid.vers = 0; + dp.dtype = 0; + dp.dev = 0; + dp.uid = "dos"; + dp.gid = "srv"; + + return dp; +} + +putdir(d: ref Dosdir, dp: ref Sys->Dir) +{ + if(dp.mode & 2) + d.attr &= byte ~DRONLY; + else + d.attr |= byte DRONLY; + + if(dp.mode & Styx->DMEXCL) + d.attr |= byte DSYSTEM; + else + d.attr &= byte ~DSYSTEM; + xputtime(d, dp.mtime); +} + +getname(arr: array of byte): string +{ + p: string; + for(i:=0; i<8; i++) { + c := int arr[i]; + if(c == 0 || c == ' ') + break; + if(i == 0 && c == 16r05) + c = 16re5; + p[len p] = c; + } + for(i=8; i<11; i++) { + c := int arr[i]; + if(c == 0 || c == ' ') + break; + if(i == 8) + p[len p] = '.'; + p[len p] = c; + } + + return p; +} + +dosname(p: string): (string, string) +{ + name := " "; + for(i := 0; i < len p && i < 8; i++) { + c := p[i]; + if(c >= 'a' && c <= 'z') + c += 'A'-'a'; + else if(c == '.') + break; + name[i] = c; + } + ext := " "; + for(j := len p - 1; j >= i; j--) { + if(p[j] == '.') { + q := 0; + for(j++; j < len p && q < 3; j++) { + c := p[j]; + if(c >= 'a' && c <= 'z') + c += 'A'-'a'; + ext[q++] = c; + } + break; + } + } + return (name, ext); +} + +putname(p: string, d: ref Dosdir) +{ + if ((int d.attr & DLONG) == DLONG) + panic("putname of long name"); + (d.name, d.ext) = dosname(p); +} + +mystrcmp(s1, s2: string): int +{ + n := len s1; + if(n != len s2) + return 1; + + for(i := 0; i < n; i++) { + c := s1[i]; + if(c >= 'A' && c <= 'Z') + c -= 'A'-'a'; + d := s2[i]; + if(d >= 'A' && d <= 'Z') + d -= 'A'-'a'; + if(c != d) + return 1; + } + return 0; +} + +# +# return the length of a long name in directory +# entries or zero if it's normal dos +# +name2de(p: string): int +{ + ext := 0; + name := 0; + + for(end := len p; --end >= 0 && p[end] != '.';) + ext++; + + if(end > 0) { + name = end; + for(i := 0; i < end; i++) { + if(p[i] == '.') + return (len p+DOSRUNES-1)/DOSRUNES; + } + } + else { + name = ext; + ext = 0; + } + + if(name <= 8 && ext <= 3 && isvalidname(p)) + return 0; + + return (len p+DOSRUNES-1)/DOSRUNES; +} + +isvalidname(s: string): int +{ + dot := 0; + for(i := 0; i < len s; i++) + if(s[i] == '.') { + if(++dot > 1 || i == len s-1) + return 0; + } else if(s[i] > len isdos || isdos[s[i]] == 0) + return 0; + return 1; +} + +getnamesect(arr: array of byte): string +{ + s: string; + c: int; + + for(i := 1; i < 11; i += 2) { + c = int arr[i] | (int arr[i+1] << 8); + if(c == 0) + return s; + s[len s] = c; + } + for(i = 14; i < 26; i += 2) { + c = int arr[i] | (int arr[i+1] << 8); + if(c == 0) + return s; + s[len s] = c; + } + for(i = 28; i < 32; i += 2) { + c = int arr[i] | (int arr[i+1] << 8); + if(c == 0) + return s; + s[len s] = c; + } + return s; +} + +# takes a long filename and converts to a short dos name, with a tag number. +long2short(src: string,val: int): string +{ + dst :=" "; + skip:=0; + xskip:=0; + ext:=len src-1; + while(ext>=0 && src[ext]!='.') + ext--; + + if (ext < 0) + ext=len src -1; + + # convert name eliding periods + j:=0; + for(name := 0; name < ext && j<8; name++){ + c := src[name]; + if(c!='.' && c!=' ' && c!='\t') { + if(c>='a' && c<='z') + dst[j++] = c-'a'+'A'; + else + dst[j++] = c; + } + else + skip++; + } + + # convert extension + j=8; + for(xname := ext+1; xname < len src && j<11; xname++) { + c := src[xname]; + if(c!=' ' && c!='\t'){ + if (c>='a' && c<='z') + dst[j++] = c-'a'+'A'; + else + dst[j++] = c; + }else + xskip++; + } + + # add tag number + j =1; + for(i:=val; i > 0; i/=10) + j++; + + if (8-j<name) + name = 8-j; + else + name -= skip; + + dst[name]='~'; + for(; val > 0; val /= 10) + dst[name+ --j] = (val%10)+'0'; + + if(debug) + chat(sys->sprint("returning dst [%s] src [%s]\n",dst,src)); + + return dst; +} + +getfat(xf: ref Xfs, n: int): int +{ + bp := xf.ptr; + k := 0; + + if(n < 2 || n >= bp.fatclusters) + return -1; + fb := bp.fatbits; + k = (fb*n) >> 3; + if(k < 0 || k >= bp.fatsize*bp.sectsize) + panic("getfat"); + + sect := k/bp.sectsize + bp.fataddr; + o := k%bp.sectsize; + p := getsect(xf, sect); + if(p == nil) + return -1; + k = int p.iobuf[o++]; + if(o >= bp.sectsize) { + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + return -1; + o = 0; + } + k |= int p.iobuf[o++]<<8; + if(fb == 32){ + # fat32 is really fat28 + k |= int p.iobuf[o++] << 16; + k |= (int p.iobuf[o] & 16r0F) << 24; + fb = 28; + } + putsect(p); + if(fb == 12) { + if(n&1) + k >>= 4; + else + k &= 16rfff; + } + + if(debug & FAT_INFO) + chat(sys->sprint("fat(0x%x)=0x%x...", n, k)); + + # + # check for out of range + # + if(k >= (1<<fb) - 8) + return -1; + return k; +} + +putfat(xf: ref Xfs, n, val: int) +{ + bp := xf.ptr; + if(n < 2 || n >= bp.fatclusters) + panic(sys->sprint("putfat n=%d", n)); + k := (bp.fatbits*n) >> 3; + if(k >= bp.fatsize*bp.sectsize) + panic("putfat"); + sect := k/bp.sectsize + bp.fataddr; + for(; sect<bp.rootaddr; sect+=bp.fatsize) { + o := k%bp.sectsize; + p := getsect(xf, sect); + if(p == nil) + continue; + case bp.fatbits { + 12 => + if(n&1) { + p.iobuf[o] &= byte 16r0f; + p.iobuf[o++] |= byte (val<<4); + if(o >= bp.sectsize) { + p.flags |= BMOD; + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + continue; + o = 0; + } + p.iobuf[o] = byte (val>>4); + } + else { + p.iobuf[o++] = byte val; + if(o >= bp.sectsize) { + p.flags |= BMOD; + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + continue; + o = 0; + } + p.iobuf[o] &= byte 16rf0; + p.iobuf[o] |= byte ((val>>8)&16r0f); + } + 16 => + p.iobuf[o++] = byte val; + p.iobuf[o] = byte (val>>8); + 32 => # fat32 is really fat28 + p.iobuf[o++] = byte val; + p.iobuf[o++] = byte (val>>8); + p.iobuf[o++] = byte (val>>16); + p.iobuf[o] = byte ((int p.iobuf[o] & 16rF0) | ((val>>24) & 16r0F)); + * => + panic("putfat fatbits"); + } + + p.flags |= BMOD; + putsect(p); + } +} + +falloc(xf: ref Xfs): int +{ + bp := xf.ptr; + n := bp.freeptr; + for(;;) { + if(getfat(xf, n) == 0) + break; + if(++n >= bp.fatclusters) + n = FATRESRV; + if(n == bp.freeptr) + return 0; + } + bp.freeptr = n+1; + if(bp.freeptr >= bp.fatclusters) + bp.freeptr = FATRESRV; + putfat(xf, n, int 16rffffffff); + k := clust2sect(bp, n); + for(i:=0; i<bp.clustsize; i++) { + p := getosect(xf, k+i); + if(p == nil) + return -1; + for(j:=0; j<len p.iobuf; j++) + p.iobuf[j] = byte 0; + p.flags = BMOD; + putsect(p); + } + return n; +} + +clust2sect(bp: ref Dosbpb, clust: int): int +{ + return bp.dataaddr + (clust - FATRESRV)*bp.clustsize; +} + +sect2clust(bp: ref Dosbpb, sect: int): int +{ + c := (sect - bp.dataaddr) / bp.clustsize + FATRESRV; + # assert(sect == clust2sect(bp, c)); + return c; +} + +bootdump(b: ref Dosboot) +{ + chat(sys->sprint("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + int b.magic[0], int b.magic[1], int b.magic[2])); + chat(sys->sprint("version: \"%8.8s\"\n", string b.version)); + chat(sys->sprint("sectsize: %d\n", bytes2short(b.sectsize))); + chat(sys->sprint("allocsize: %d\n", int b.clustsize)); + chat(sys->sprint("nresrv: %d\n", bytes2short(b.nresrv))); + chat(sys->sprint("nfats: %d\n", int b.nfats)); + chat(sys->sprint("rootsize: %d\n", bytes2short(b.rootsize))); + chat(sys->sprint("volsize: %d\n", bytes2short(b.volsize))); + chat(sys->sprint("mediadesc: 0x%2.2x\n", int b.mediadesc)); + chat(sys->sprint("fatsize: %d\n", bytes2short(b.fatsize))); + chat(sys->sprint("trksize: %d\n", bytes2short(b.trksize))); + chat(sys->sprint("nheads: %d\n", bytes2short(b.nheads))); + chat(sys->sprint("nhidden: %d\n", bytes2int(b.nhidden))); + chat(sys->sprint("bigvolsize: %d\n", bytes2int(b.bigvolsize))); + chat(sys->sprint("driveno: %d\n", int b.driveno)); + chat(sys->sprint("bootsig: 0x%2.2x\n", int b.bootsig)); + chat(sys->sprint("volid: 0x%8.8x\n", bytes2int(b.volid))); + chat(sys->sprint("label: \"%11.11s\"\n", string b.label)); +} + +xputtime(d: ref Dosdir, s: int) +{ + if(s == 0) + t := daytime->local((sys->millisec() - nowt1)/1000 + nowt); + else + t = daytime->local(s); + x := (t.hour<<11) | (t.min<<5) | (t.sec>>1); + d.time[0] = byte x; + d.time[1] = byte (x>>8); + x = ((t.year-80)<<9) | ((t.mon+1)<<5) | t.mday; + d.date[0] = byte x; + d.date[1] = byte (x>>8); +} + +puttime(d: ref Dosdir) +{ + xputtime(d, 0); +} + +gtime(a: array of byte): int +{ + tm := ref Daytime->Tm; + i := bytes2short(a[22:24]); # dos time + tm.hour = i >> 11; + tm.min = (i>>5) & 63; + tm.sec = (i & 31) << 1; + i = bytes2short(a[24:26]); # dos date + tm.year = 80 + (i>>9); + tm.mon = ((i>>5) & 15) - 1; + tm.mday = i & 31; + tm.tzoff = tzoff; # DOS time is local time + return daytime->tm2epoch(tm); +} + +dirdump(arr: array of byte, addr, offset: int) +{ + if(!debug) + return; + attrchar:= "rhsvda67"; + d := Dosdir.arr2Dd(arr); + buf := sys->sprint("\"%.8s.%.3s\" ", d.name, d.ext); + p_i:=7; + + for(i := 16r80; i != 0; i >>= 1) { + if((d.attr & byte i) == byte i) + ch := attrchar[p_i]; + else + ch = '-'; + buf += sys->sprint("%c", ch); + p_i--; + } + + i = bytes2short(d.time); + buf += sys->sprint(" %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); + i = bytes2short(d.date); + buf += sys->sprint(" %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); + buf += sys->sprint(" %d %d", bytes2short(d.start), bytes2short(d.length)); + buf += sys->sprint(" %d %d\n",addr,offset); + chat(buf); +} + +putnamesect(longname: string, curslot: int, first: int, sum: int, a: array of byte) +{ + for(i := 0; i < DOSDIRSIZE; i++) + a[i] = byte 16rFF; + if(first) + a[0] = byte (16r40 | curslot); + else + a[0] = byte curslot; + a[11] = byte DLONG; + a[12] = byte 0; + a[13] = byte sum; + a[26] = byte 0; + a[27] = byte 0; + # a[1:1+10] = characters 1 to 5 + n := len longname; + j := (curslot-1)*DOSRUNES; + for(i = 1; i < 1+10; i += 2){ + c := 0; + if(j < n) + c = longname[j++]; + a[i] = byte c; + a[i+1] = byte (c >> 8); + if(c == 0) + return; + } + # a[14:14+12] = characters 6 to 11 + for(i = 14; i < 14+12; i += 2){ + c := 0; + if(j < n) + c = longname[j++]; + a[i] = byte c; + a[i+1] = byte (c >> 8); + if(c == 0) + return; + } + # a[28:28+4] characters 12 to 13 + for(i = 28; i < 28+4; i += 2){ + c := 0; + if(j < n) + c = longname[j++]; + a[i] = byte c; + a[i+1] = byte (c>>8); + if(c == 0) + return; + } +} + +putlongname(xf: ref Xfs, ndp: ref Dosptr, name: string, sname: string): int +{ + bp := xf.ptr; + first := 1; + sum := aliassum(sname); + for(nds := (len name+DOSRUNES-1)/DOSRUNES; nds > 0; nds--) { + putnamesect(name, nds, first, sum, ndp.p.iobuf[ndp.offset:]); + first = 0; + ndp.offset += DOSDIRSIZE; + if(ndp.offset == bp.sectsize) { + if(debug) + chat(sys->sprint("long name %s entry %d/%d crossing sector, addr=%d, naddr=%d", name, nds, (len name+DOSRUNES-1)/DOSRUNES, ndp.addr, ndp.naddr)); + ndp.p.flags |= BMOD; + putsect(ndp.p); + ndp.p = nil; + ndp.d = nil; + + # switch to the next cluster for the next long entry or the subsequent normal dir. entry + # naddr must be set up correctly by searchdir because we'll need one or the other + + ndp.prevaddr = ndp.addr; + ndp.addr = ndp.naddr; + ndp.naddr = -1; + if(ndp.addr < 0) + return -1; + ndp.p = getsect(xf, ndp.addr); + if(ndp.p == nil) + return -1; + ndp.offset = 0; + } + } + return 0; +} + +bytes2int(a: array of byte): int +{ + return (((((int a[3] << 8) | int a[2]) << 8) | int a[1]) << 8) | int a[0]; +} + +bytes2short(a: array of byte): int +{ + return (int a[1] << 8) | int a[0]; +} + +chat(s: string) +{ + if(debug) + sys->fprint(sys->fildes(2), "%s", s); +} + +panic(s: string) +{ + sys->fprint(sys->fildes(2), "dosfs: panic: %s\n", s); + if(pflag) + <-chan of int; # hang here + raise "fail:panic"; +} + +Dosboot.arr2Db(arr: array of byte): ref Dosboot +{ + db := ref Dosboot; + db.magic = arr[0:3]; + db.version = arr[3:11]; + db.sectsize = arr[11:13]; + db.clustsize = arr[13]; + db.nresrv = arr[14:16]; + db.nfats = arr[16]; + db.rootsize = arr[17:19]; + db.volsize = arr[19:21]; + db.mediadesc = arr[21]; + db.fatsize = arr[22:24]; + db.trksize = arr[24:26]; + db.nheads = arr[26:28]; + db.nhidden = arr[28:32]; + db.bigvolsize = arr[32:36]; + db.driveno = arr[36]; + db.bootsig = arr[38]; + db.volid = arr[39:43]; + db.label = arr[43:54]; + return db; +} + +Dosdir.arr2Dd(arr: array of byte): ref Dosdir +{ + dir := ref Dosdir; + for(i := 0; i < 8; i++) + dir.name[len dir.name] = int arr[i]; + for(; i < 11; i++) + dir.ext[len dir.ext] = int arr[i]; + dir.attr = arr[11]; + dir.reserved = arr[12:22]; + dir.time = arr[22:24]; + dir.date = arr[24:26]; + dir.start = arr[26:28]; + dir.length = arr[28:32]; + return dir; +} + +Dosdir.Dd2arr(d: ref Dosdir): array of byte +{ + a := array[32] of byte; + i:=0; + for(j := 0; j < len d.name; j++) + a[i++] = byte d.name[j]; + for(; j<8; j++) + a[i++]= byte 0; + for(j=0; j<len d.ext; j++) + a[i++] = byte d.ext[j]; + for(; j<3; j++) + a[i++]= byte 0; + a[i++] = d.attr; + for(j=0; j<10; j++) + a[i++] = d.reserved[j]; + for(j=0; j<2; j++) + a[i++] = d.time[j]; + for(j=0; j<2; j++) + a[i++] = d.date[j]; + for(j=0; j<2; j++) + a[i++] = d.start[j]; + for(j=0; j<4; j++) + a[i++] = d.length[j]; + return a; +} + +# +# checksum of short name for use in long name directory entries +# assumes sname is already padded correctly to 8+3 +# +aliassum(sname: string): int +{ + i := 0; + for(sum:=0; i<11; i++) + sum = (((sum&1)<<7)|((sum&16rfe)>>1))+sname[i]; + return sum; +} + +# +# track i/o +# + +# An Xfs represents the root of an external file system, anchored +# to the server and the client +Xfs: adt { + next:cyclic ref Xfs; + name: string; # of file containing external f.s. + qid: Sys->Qid; # of file containing external f.s. + refn: int; # attach count + rootqid: Sys->Qid; # of inferno constructed root directory + dev: ref Sys->FD; # FD of the file containing external f.s. + fmt: int; # successfully read format + offset: int; # offset in sectors to file system + ptr: ref Dosbpb; +}; + +# An Xfile represents the mapping of fid's & qid's to the server. +Xfile: adt { + next: cyclic ref Xfile; # in hash bucket + client: int; + fid: int; + flags: int; + qid: Sys->Qid; + xf: ref Xfs; + ptr: ref Dosptr; +}; + +Iosect: adt +{ + next: cyclic ref Iosect; + flags: int; + t: cyclic ref Iotrack; + iobuf: array of byte; +}; + +Iotrack: adt +{ + flags: int; + xf: ref Xfs; + addr: int; + next: cyclic ref Iotrack; # in lru list + prev: cyclic ref Iotrack; + hnext: cyclic ref Iotrack; # in hash list + hprev: cyclic ref Iotrack; + refn: int; + tp: cyclic ref Track; +}; + +Track: adt +{ + create: fn(): ref Track; + p: cyclic array of ref Iosect; + buf: array of byte; +}; + +BMOD: con 1<<0; +BIMM: con 1<<1; +BSTALE: con 1<<2; + +HIOB: con 31; # a prime +NIOBUF: con 20; + +Sectorsize: con 512; +Sect2trk: con 9; # default + +hiob := array[HIOB+1] of ref Iotrack; # hash buckets + lru list +iobuf := array[NIOBUF] of ref Iotrack; # the real ones +freelist: ref Iosect; +sect2trk := Sect2trk; +trksize := Sect2trk*Sectorsize; + +FIDMOD: con 127; # prime +xhead: ref Xfs; +client: int; + +xfiles := array[FIDMOD] of ref Xfile; +iodebug := 0; + +iotrackinit(sectors: int) +{ + if(sectors <= 0) + sectors = 9; + sect2trk = sectors; + trksize = sect2trk*Sectorsize; + + freelist = nil; + + for(i := 0;i < FIDMOD; i++) + xfiles[i] = ref Xfile(nil,0,0,0,Sys->Qid(big 0,0,0),nil,nil); + + for(i = 0; i <= HIOB; i++) + hiob[i] = ref Iotrack; + + for(i = 0; i < HIOB; i++) { + hiob[i].hprev = hiob[i]; + hiob[i].hnext = hiob[i]; + hiob[i].refn = 0; + hiob[i].addr = 0; + } + hiob[i].prev = hiob[i]; + hiob[i].next = hiob[i]; + hiob[i].refn = 0; + hiob[i].addr = 0; + + for(i=0;i<NIOBUF;i++) + iobuf[i] = ref Iotrack; + + for(i=0; i<NIOBUF; i++) { + iobuf[i].hprev = iobuf[i].hnext = iobuf[i]; + iobuf[i].prev = iobuf[i].next = iobuf[i]; + iobuf[i].refn=iobuf[i].addr=0; + iobuf[i].flags = 0; + if(hiob[HIOB].next != iobuf[i]) { + iobuf[i].prev.next = iobuf[i].next; + iobuf[i].next.prev = iobuf[i].prev; + iobuf[i].next = hiob[HIOB].next; + iobuf[i].prev = hiob[HIOB]; + hiob[HIOB].next.prev = iobuf[i]; + hiob[HIOB].next = iobuf[i]; + } + iobuf[i].tp = Track.create(); + } +} + +Track.create(): ref Track +{ + t := ref Track; + t.p = array[sect2trk] of ref Iosect; + t.buf = array[trksize] of byte; + return t; +} + +getsect(xf: ref Xfs, addr: int): ref Iosect +{ + return getiosect(xf, addr, 1); +} + +getosect(xf: ref Xfs, addr: int): ref Iosect +{ + return getiosect(xf, addr, 0); +} + +# get the sector corresponding to the address addr. +getiosect(xf: ref Xfs, addr , rflag: int): ref Iosect +{ + # offset from beginning of track. + toff := addr % sect2trk; + + # address of beginning of track. + taddr := addr - toff; + t := getiotrack(xf, taddr); + + if(rflag && t.flags&BSTALE) { + if(tread(t) < 0) + return nil; + + t.flags &= ~BSTALE; + } + + t.refn++; + if(t.tp.p[toff] == nil) { + p := newsect(); + t.tp.p[toff] = p; + p.flags = t.flags&BSTALE; + p.t = t; + p.iobuf = t.tp.buf[toff*Sectorsize:(toff+1)*Sectorsize]; + } + return t.tp.p[toff]; +} + +putsect(p: ref Iosect) +{ + t: ref Iotrack; + + t = p.t; + t.flags |= p.flags; + p.flags = 0; + t.refn--; + if(t.refn < 0) + panic("putsect: refcount"); + + if(t.flags & BIMM) { + if(t.flags & BMOD) + twrite(t); + t.flags &= ~(BMOD|BIMM); + } +} + +# get the track corresponding to addr +# (which is the address of the beginning of a track +getiotrack(xf: ref Xfs, addr: int): ref Iotrack +{ + p: ref Iotrack; + mp := hiob[HIOB]; + + if(iodebug) + chat(sys->sprint("iotrack %d,%d...", xf.dev.fd, addr)); + + # find bucket in hash table. + h := (xf.dev.fd<<24) ^ addr; + if(h < 0) + h = ~h; + h %= HIOB; + hp := hiob[h]; + + out: for(;;){ + loop: for(;;) { + # look for it in the active list + for(p = hp.hnext; p != hp; p=p.hnext) { + if(p.addr != addr || p.xf != xf) + continue; + if(p.addr == addr && p.xf == xf) { + break out; + } + continue loop; + } + + # not found + # take oldest unref'd entry + for(p = mp.prev; p != mp; p=p.prev) + if(p.refn == 0 ) + break; + if(p == mp) { + if(iodebug) + chat("iotrack all ref'd\n"); + continue loop; + } + + if((p.flags & BMOD)!= 0) { + twrite(p); + p.flags &= ~(BMOD|BIMM); + continue loop; + } + purgetrack(p); + p.addr = addr; + p.xf = xf; + p.flags = BSTALE; + break out; + } + } + + if(hp.hnext != p) { + p.hprev.hnext = p.hnext; + p.hnext.hprev = p.hprev; + p.hnext = hp.hnext; + p.hprev = hp; + hp.hnext.hprev = p; + hp.hnext = p; + } + if(mp.next != p) { + p.prev.next = p.next; + p.next.prev = p.prev; + p.next = mp.next; + p.prev = mp; + mp.next.prev = p; + mp.next = p; + } + return p; +} + +purgetrack(t: ref Iotrack) +{ + refn := sect2trk; + for(i := 0; i < sect2trk; i++) { + if(t.tp.p[i] == nil) { + --refn; + continue; + } + freesect(t.tp.p[i]); + --refn; + t.tp.p[i]=nil; + } + if(t.refn != refn) + panic("purgetrack"); + if(refn!=0) + panic("refn not 0"); +} + +twrite(t: ref Iotrack): int +{ + if(iodebug) + chat(sys->sprint("[twrite %d...", t.addr)); + + if((t.flags & BSTALE)!= 0) { + refn:=0; + for(i:=0; i<sect2trk; i++) + if(t.tp.p[i]!=nil) + ++refn; + + if(refn < sect2trk) { + if(tread(t) < 0) { + if (iodebug) + chat("error]"); + return -1; + } + } + else + t.flags &= ~BSTALE; + } + + if(devwrite(t.xf, t.addr, t.tp.buf) < 0) { + if(iodebug) + chat("error]"); + return -1; + } + + if(iodebug) + chat(" done]"); + + return 0; +} + +tread(t: ref Iotrack): int +{ + refn := 0; + rval: int; + + for(i := 0; i < sect2trk; i++) + if(t.tp.p[i] != nil) + ++refn; + + if(iodebug) + chat(sys->sprint("[tread %d...", t.addr)); + + tbuf := t.tp.buf; + if(refn != 0) + tbuf = array[trksize] of byte; + + rval = devread(t.xf, t.addr, tbuf); + if(rval < 0) { + if(iodebug) + chat("error]"); + return -1; + } + + if(refn != 0) { + for(i=0; i < sect2trk; i++) { + if(t.tp.p[i] == nil) { + t.tp.buf[i*Sectorsize:]=tbuf[i*Sectorsize:(i+1)*Sectorsize]; + if(iodebug) + chat(sys->sprint("%d ", i)); + } + } + } + + if(iodebug) + chat("done]"); + + t.flags &= ~BSTALE; + return 0; +} + +purgebuf(xf: ref Xfs) +{ + for(p := 0; p < NIOBUF; p++) { + if(iobuf[p].xf != xf) + continue; + if(iobuf[p].xf == xf) { + if((iobuf[p].flags & BMOD) != 0) + twrite(iobuf[p]); + + iobuf[p].flags = BSTALE; + purgetrack(iobuf[p]); + } + } +} + +sync() +{ + for(p := 0; p < NIOBUF; p++) { + if(!(iobuf[p].flags & BMOD)) + continue; + + if(iobuf[p].flags & BMOD){ + twrite(iobuf[p]); + iobuf[p].flags &= ~(BMOD|BIMM); + } + } +} + + +newsect(): ref Iosect +{ + if((p := freelist)!=nil) { + freelist = p.next; + p.next = nil; + } else + p = ref Iosect(nil, 0, nil,nil); + + return p; +} + +freesect(p: ref Iosect) +{ + p.next = freelist; + freelist = p; +} + + +# devio from here +deverror(name: string, xf: ref Xfs, addr,n,nret: int): int +{ + if(nret < 0) { + if(iodebug) + chat(sys->sprint("%s errstr=\"%r\"...", name)); + xf.dev = nil; + return -1; + } + if(iodebug) + chat(sys->sprint("dev %d sector %d, %s: %d, should be %d\n", + xf.dev.fd, addr, name, nret, n)); + + panic(name); + return -1; +} + +devread(xf: ref Xfs, addr: int, buf: array of byte): int +{ + if(xf.dev==nil) + return -1; + + sys->seek(xf.dev, big (xf.offset+addr*Sectorsize), sys->SEEKSTART); + nread := sys->read(xf.dev, buf, trksize); + if(nread != trksize) + return deverror("read", xf, addr, trksize, nread); + + return 0; +} + +devwrite(xf: ref Xfs, addr: int, buf: array of byte): int +{ + if(xf.dev == nil) + return -1; + + sys->seek(xf.dev, big (xf.offset+addr*Sectorsize), 0); + nwrite := sys->write(xf.dev, buf, trksize); + if(nwrite != trksize) + return deverror("write", xf, addr, trksize , nwrite); + + return 0; +} + +devcheck(xf: ref Xfs): int +{ + buf := array[Sectorsize] of byte; + + if(xf.dev == nil) + return -1; + + sys->seek(xf.dev, big 0, sys->SEEKSTART); + if(sys->read(xf.dev, buf, Sectorsize) != Sectorsize){ + xf.dev = nil; + return -1; + } + + return 0; +} + +# setup and return the Xfs associated with "name" + +getxfs(name: string): (ref Xfs, string) +{ + if(name == nil) + return (nil, "no file system device specified"); + + + # If the name passed is of the form 'name:offset' then + # offset is used to prime xf->offset. This allows accessing + # a FAT-based filesystem anywhere within a partition. + # Typical use would be to mount a filesystem in the presence + # of a boot manager programm at the beginning of the disc. + + offset := 0; + for(i := 0;i < len name; i++) + if(name[i]==':') + break; + + if(i < len name) { + offset = int name[i+1:]; + if(offset < 0) + return (nil, "invalid device offset to file system"); + offset *= Sectorsize; + name = name[0:i]; + } + + fd := sys->open(name, Sys->ORDWR); + if(fd == nil) { + if(iodebug) + chat(sys->sprint("getxfs: open(%s) failed: %r\n", name)); + return (nil, sys->sprint("can't open %s: %r", name)); + } + + (rval,dir) := sys->fstat(fd); + if(rval < 0) + return (nil, sys->sprint("can't stat %s: %r", name)); + + # lock down the list of xf's. + fxf: ref Xfs; + for(xf := xhead; xf != nil; xf = xf.next) { + if(xf.refn == 0) { + if(fxf == nil) + fxf = xf; + continue; + } + if(xf.qid.path != dir.qid.path || xf.qid.vers != dir.qid.vers) + continue; + + if(xf.name!= name || xf.dev == nil) + continue; + + if(devcheck(xf) < 0) # look for media change + continue; + + if(offset && xf.offset != offset) + continue; + + if(iodebug) + chat(sys->sprint("incref \"%s\", dev=%d...", + xf.name, xf.dev.fd)); + + ++xf.refn; + return (xf, nil); + } + + # this xf doesn't exist, make a new one and stick it on the list. + if(fxf == nil){ + fxf = ref Xfs; + fxf.next = xhead; + xhead = fxf; + } + + if(iodebug) + chat(sys->sprint("alloc \"%s\", dev=%d...", name, fd.fd)); + + fxf.name = name; + fxf.refn = 1; + fxf.qid = dir.qid; + fxf.dev = fd; + fxf.fmt = 0; + fxf.offset = offset; + return (fxf, nil); +} + +refxfs(xf: ref Xfs, delta: int) +{ + xf.refn += delta; + if(xf.refn == 0) { + if (iodebug) + chat(sys->sprint("free \"%s\", dev=%d...", + xf.name, xf.dev.fd)); + + purgebuf(xf); + if(xf.dev !=nil) + xf.dev = nil; + } +} + +xfile(fid, flag: int): ref Xfile +{ + pf: ref Xfile; + + # find hashed file list in LRU? table. + k := (fid^client)%FIDMOD; + + # find if this fid is in the hashed file list. + f:=xfiles[k]; + for(pf = nil; f != nil; f = f.next) { + if(f.fid == fid && f.client == client) + break; + pf=f; + } + + # move this fid to the front of the list if it was further down. + if(f != nil && pf != nil){ + pf.next = f.next; + f.next = xfiles[k]; + xfiles[k] = f; + } + + case flag { + * => + panic("xfile"); + Asis => + if(f != nil && f.xf != nil && f.xf.dev == nil) + return nil; + return f; + Clean => + break; + Clunk => + if(f != nil) { + xfiles[k] = f.next; + clean(f); + } + return nil; + } + + # clean it up .. + if(f != nil) + return clean(f); + + # f wasn't found in the hashtable, make a new one and add it + f = ref Xfile; + f.next = xfiles[k]; + xfiles[k] = f; + # sort out the fid, etc. + f.fid = fid; + f.client = client; + f.flags = 0; + f.qid = Sys->Qid(big 0, 0, Styx->QTFILE); + f.xf = nil; + f.ptr = ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); + return f; +} + +clean(f: ref Xfile): ref Xfile +{ + f.ptr = nil; + if(f.xf != nil) { + refxfs(f.xf, -1); + f.xf = nil; + } + f.flags = 0; + f.qid = Sys->Qid(big 0, 0, 0); + return f; +} + +# +# the file at <addr, offset> has moved +# relocate the dos entries of all fids in the same file +# +dosptrreloc(f: ref Xfile, dp: ref Dosptr, addr: int, offset: int) +{ + i: int; + p: ref Xfile; + xdp: ref Dosptr; + + for(i=0; i < FIDMOD; i++){ + for(p = xfiles[i]; p != nil; p = p.next){ + xdp = p.ptr; + if(p != f && p.xf == f.xf + && xdp != nil && xdp.addr == addr && xdp.offset == offset){ + *xdp = *dp; + xdp.p = nil; + # xdp.d = nil; + p.qid.path = big QIDPATH(xdp); + } + } + } +} |
