ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/dossrv.b/
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);
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(sys->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);
}
}
}
}