ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/9660srv.b/
implement ISO9660;
include "sys.m";
sys: Sys;
Dir, Qid, QTDIR, QTFILE, DMDIR: import sys;
include "draw.m";
include "daytime.m";
daytime: Daytime;
include "string.m";
str: String;
include "styx.m";
styx: Styx;
Rmsg, Tmsg: import styx;
include "arg.m";
ISO9660: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
Sectorsize: con 2048;
Maxname: con 256;
Enonexist: con "file does not exist";
Eperm: con "permission denied";
Enofile: con "no file system specified";
Eauth: con "authentication failed";
Ebadfid: con "invalid fid";
Efidinuse: con "fid already in use";
Enotdir: con "not a directory";
Esyntax: con "file name syntax";
devname: string;
chatty := 0;
showstyx := 0;
progname := "9660srv";
stderr: ref Sys->FD;
noplan9 := 0;
nojoliet := 0;
norock := 0;
usage()
{
sys->fprint(sys->fildes(2), "usage: %s [-rabc] [-9JR] [-s] cd_device dir\n", progname);
raise "fail:usage";
}
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);
stderr = sys->fildes(2);
if(args != nil)
progname = hd args;
styx = load Styx Styx->PATH;
if(styx == nil)
noload(Styx->PATH);
styx->init();
if(args != nil)
progname = hd args;
mountopt := Sys->MREPL;
copt := 0;
stdio := 0;
arg := load Arg Arg->PATH;
if(arg == nil)
noload(Arg->PATH);
arg->init(args);
while((c := arg->opt()) != 0)
case c {
'v' or 'D' => chatty = 1; showstyx = 1;
'r' => mountopt = Sys->MREPL;
'a' => mountopt = Sys->MAFTER;
'b' => mountopt = Sys->MBEFORE;
'c' => copt = Sys->MCREATE;
's' => stdio = 1;
'9' => noplan9 = 1;
'J' => nojoliet = 1;
'R' => norock = 1;
* => usage();
}
args = arg->argv();
arg = nil;
if(args == nil || tl args == nil)
usage();
what := hd args;
mountpt := hd tl args;
daytime = load Daytime Daytime->PATH;
if(daytime == nil)
noload(Daytime->PATH);
iobufinit(Sectorsize);
pip := array[2] of ref Sys->FD;
if(stdio){
pip[0] = sys->fildes(0);
pip[1] = sys->fildes(1);
}else
if(sys->pipe(pip) < 0)
error(sys->sprint("can't create pipe: %r"));
devname = what;
sync := chan of int;
spawn fileserve(pip[1], sync);
<-sync;
if(sys->mount(pip[0], nil, mountpt, mountopt|copt, nil) < 0) {
sys->fprint(sys->fildes(2), "%s: mount %s %s failed: %r\n", progname, what, mountpt);
exit;
}
}
noload(s: string)
{
sys->fprint(sys->fildes(2), "%s: can't load %s: %r\n", progname, s);
raise "fail:load";
}
error(p: string)
{
sys->fprint(sys->fildes(2), "9660srv: %s\n", p);
raise "fail:error";
}
fileserve(rfd: ref Sys->FD, sync: chan of int)
{
sys->pctl(Sys->NEWFD|Sys->FORKNS, list of {2, rfd.fd});
rfd = sys->fildes(rfd.fd);
stderr = sys->fildes(2);
sync <-= 1;
while((m := Tmsg.read(rfd, 0)) != nil){
if(showstyx)
chat(sys->sprint("%s...", m.text()));
r: ref Rmsg;
pick t := m {
Readerror =>
error(sys->sprint("mount read error: %s", t.error));
Version =>
r = rversion(t);
Auth =>
r = rauth(t);
Flush =>
r = rflush(t);
Attach =>
r = rattach(t);
Walk =>
r = rwalk(t);
Open =>
r = ropen(t);
Create =>
r = rcreate(t);
Read =>
r = rread(t);
Write =>
r = rwrite(t);
Clunk =>
r = rclunk(t);
Remove =>
r = rremove(t);
Stat =>
r = rstat(t);
Wstat =>
r = rwstat(t);
* =>
error(sys->sprint("invalid T-message tag: %d", tagof m));
}
pick e := r {
Error =>
r.tag = m.tag;
}
rbuf := r.pack();
if(rbuf == nil)
error("bad R-message conversion");
if(showstyx)
chat(r.text()+"\n");
if(sys->write(rfd, rbuf, len rbuf) != len rbuf)
error(sys->sprint("connection write error: %r"));
}
if(chatty)
chat("server end of file\n");
}
E(s: string): ref Rmsg.Error
{
return ref Rmsg.Error(0, s);
}
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
{
dname := devname;
if(t.aname != "")
dname = t.aname;
(dev, err) := devattach(dname, Sys->OREAD, Sectorsize);
if(dev == nil)
return E(err);
xf := Xfs.new(dev);
root := cleanfid(t.fid);
root.qid = Sys->Qid(big 0, 0, Sys->QTDIR);
root.xf = xf;
err = root.attach();
if(err != nil){
clunkfid(t.fid);
return E(err);
}
xf.rootqid = root.qid;
return ref Rmsg.Attach(t.tag, root.qid);
}
walk1(f: ref Xfile, name: string): string
{
if(!(f.qid.qtype & Sys->QTDIR))
return Enotdir;
case name {
"." =>
return nil; # nop, but shouldn't happen
".." =>
if(f.qid.path==f.xf.rootqid.path)
return nil;
return f.walkup();
* =>
return f.walk(name);
}
}
rwalk(t: ref Tmsg.Walk): ref Rmsg
{
f:=findfid(t.fid);
if(f == nil)
return E(Ebadfid);
nf, sf: ref Xfile;
if(t.newfid != t.fid){
nf = cleanfid(t.newfid);
if(nf == nil)
return E(Efidinuse);
f.clone(nf);
f = nf;
}else
sf = f.save();
qids: array of Sys->Qid;
if(len t.names > 0){
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){
if(nf != nil){
nf.clunk();
clunkfid(t.newfid);
}else
f.restore(sf);
if(i == 0)
return E(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
{
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
if(f.flags&Omodes)
return E("open on open file");
e := f.open(t.mode);
if(e != nil)
return E(e);
f.flags = openflags(t.mode);
return ref Rmsg.Open(t.tag, f.qid, Styx->MAXFDATA);
}
rcreate(t: ref Tmsg.Create): ref Rmsg
{
name := t.name;
if(name == "." || name == "..")
return E(Esyntax);
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
if(f.flags&Omodes)
return E("create on open file");
if(!(f.qid.qtype&Sys->QTDIR))
return E("create in non-directory");
e := f.create(name, t.perm, t.mode);
if(e != nil)
return E(e);
f.flags = openflags(t.mode);
return ref Rmsg.Create(t.tag, f.qid, Styx->MAXFDATA);
}
rread(t: ref Tmsg.Read): ref Rmsg
{
err: string;
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
if (!(f.flags&Oread))
return E("file not opened for reading");
if(t.count < 0 || t.offset < big 0)
return E("negative offset or count");
b := array[Styx->MAXFDATA] of byte;
count: int;
if(f.qid.qtype & Sys->QTDIR)
(count, err) = f.readdir(b, int t.offset, t.count);
else
(count, err) = f.read(b, int t.offset, t.count);
if(err != nil)
return E(err);
if(count != len b)
b = b[0:count];
return ref Rmsg.Read(t.tag, b);
}
rwrite(nil: ref Tmsg.Write): ref Rmsg
{
return E(Eperm);
}
rclunk(t: ref Tmsg.Clunk): ref Rmsg
{
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
f.clunk();
clunkfid(t.fid);
return ref Rmsg.Clunk(t.tag);
}
rremove(t: ref Tmsg.Remove): ref Rmsg
{
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
f.clunk();
clunkfid(t.fid);
return E(Eperm);
}
rstat(t: ref Tmsg.Stat): ref Rmsg
{
f := findfid(t.fid);
if(f == nil)
return E(Ebadfid);
(dir, nil) := f.stat();
return ref Rmsg.Stat(t.tag, *dir);
}
rwstat(nil: ref Tmsg.Wstat): ref Rmsg
{
return E(Eperm);
}
openflags(mode: int): int
{
flags := 0;
case mode & ~(Sys->OTRUNC|Sys->ORCLOSE) {
Sys->OREAD =>
flags = Oread;
Sys->OWRITE =>
flags = Owrite;
Sys->ORDWR =>
flags = Oread|Owrite;
}
if(mode & Sys->ORCLOSE)
flags |= Orclose;
return flags;
}
chat(s: string)
{
if(chatty)
sys->fprint(stderr, "%s", s);
}
Fid: adt {
fid: int;
file: ref Xfile;
};
FIDMOD: con 127; # prime
fids := array[FIDMOD] of list of ref Fid;
hashfid(fid: int): (ref Fid, array of list of ref Fid)
{
nl: list of ref Fid;
hp := fids[fid%FIDMOD:];
nl = nil;
for(l := hp[0]; l != nil; l = tl l){
f := hd l;
if(f.fid == fid){
l = tl l; # excluding f
for(; nl != nil; nl = tl nl)
l = (hd nl) :: l; # put examined ones back, in order
hp[0] = l;
return (f, hp);
} else
nl = f :: nl;
}
return (nil, hp);
}
findfid(fid: int): ref Xfile
{
(f, hp) := hashfid(fid);
if(f == nil){
chat("unassigned fid");
return nil;
}
hp[0] = f :: hp[0];
return f.file;
}
cleanfid(fid: int): ref Xfile
{
(f, hp) := hashfid(fid);
if(f != nil){
chat("fid in use");
return nil;
}
f = ref Fid;
f.fid = fid;
f.file = Xfile.new();
hp[0] = f :: hp[0];
return f.file.clean();
}
clunkfid(fid: int)
{
(f, nil) := hashfid(fid);
if(f != nil)
f.file.clean();
}
#
#
#
Xfs: adt {
d: ref Device;
inuse: int;
issusp: int; # system use sharing protocol in use?
suspoff: int; # LEN_SKP, if so
isplan9: int; # has Plan 9-specific directory info
isrock: int; # is rock ridge
rootqid: Sys->Qid;
ptr: int; # tag for private data
new: fn(nil: ref Device): ref Xfs;
incref: fn(nil: self ref Xfs);
decref: fn(nil: self ref Xfs);
};
Xfile: adt {
xf: ref Xfs;
flags: int;
qid: Sys->Qid;
ptr: ref Isofile; # tag for private data
new: fn(): ref Xfile;
clean: fn(nil: self ref Xfile): ref Xfile;
save: fn(nil: self ref Xfile): ref Xfile;
restore: fn(nil: self ref Xfile, s: ref Xfile);
attach: fn(nil: self ref Xfile): string;
clone: fn(nil: self ref Xfile, nil: ref Xfile);
walkup: fn(nil: self ref Xfile): string;
walk: fn(nil: self ref Xfile, nil: string): string;
open: fn(nil: self ref Xfile, nil: int): string;
create: fn(nil: self ref Xfile, nil: string, nil: int, nil: int): string;
readdir: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string);
read: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string);
write: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string);
clunk: fn(nil: self ref Xfile);
remove: fn(nil: self ref Xfile): string;
stat: fn(nil: self ref Xfile): (ref Sys->Dir, string);
wstat: fn(nil: self ref Xfile, nil: ref Sys->Dir): string;
};
Oread, Owrite, Orclose: con 1<<iota;
Omodes: con 3; # mask
VOLDESC: con 16; # sector number
Drec: adt {
reclen: int;
attrlen: int;
addr: int; # should be big?
size: int; # should be big?
date: array of byte;
time: int;
tzone: int; # not in high sierra
flags: int;
unitsize: int;
gapsize: int;
vseqno: int;
name: array of byte;
data: array of byte; # system extensions
};
Isofile: adt {
fmt: int; # 'z' if iso, 'r' if high sierra
blksize: int;
offset: int; # true offset when reading directory
doffset: int; # styx offset when reading directory
d: ref Drec;
};
Xfile.new(): ref Xfile
{
f := ref Xfile;
return f.clean();
}
Xfile.clean(f: self ref Xfile): ref Xfile
{
if(f.xf != nil){
f.xf.decref();
f.xf = nil;
}
f.ptr = nil;
f.flags = 0;
f.qid = Qid(big 0, 0, 0);
return f;
}
Xfile.save(f: self ref Xfile): ref Xfile
{
s := ref Xfile;
*s = *f;
s.ptr = ref *f.ptr;
s.ptr.d = ref *f.ptr.d;
return s;
}
Xfile.restore(f: self ref Xfile, s: ref Xfile)
{
f.flags = s.flags;
f.qid = s.qid;
*f.ptr = *s.ptr;
}
Xfile.attach(root: self ref Xfile): string
{
fmt := 0;
blksize := 0;
haveplan9 := 0;
dirp: ref Block;
dp := ref Drec;
for(a:=VOLDESC;a<VOLDESC+100;a++){
p := Block.get(root.xf.d, a);
if(p == nil){
if(dirp != nil)
dirp.put();
return "can't read volume descriptor";
}
v := p.data; # Voldesc
if(eqs(v[0:7], "\u0001CD001\u0001")){ # ISO
if(dirp != nil)
dirp.put();
dirp = p;
fmt = 'z';
convM2Drec(v[156:], dp, 0); # v.z.desc.rootdir
blksize = l16(v[128:]); # v.z.desc.blksize
if(chatty)
chat(sys->sprint("iso, blksize=%d...", blksize));
haveplan9 = eqs(v[8:8+6], "PLAN 9"); # v.z.boot.sysid
if(haveplan9){
if(noplan9) {
chat("ignoring plan9");
haveplan9 = 0;
}else{
fmt = '9';
chat("plan9 iso...");
}
}
continue;
}
if(eqs(v[8:8+7], "\u0001CDROM\u0001")){ # high sierra
if(dirp != nil)
dirp.put();
dirp = p;
fmt = 'r';
convM2Drec(v[180:], dp, 1); # v.r.desc.rootdir
blksize = l16(v[136:]); # v.r.desc.blksize
if(chatty)
chat(sys->sprint("high sierra, blksize=%d...", blksize));
continue;
}
if(haveplan9==0 && !nojoliet && eqs(v[0:7], "\u0002CD001\u0001")){
q := v[88:]; # v.z.desc.escapes
if(q[0] == byte 16r25 && q[1] == byte 16r2F &&
(q[2] == byte 16r40 || q[2] == byte 16r43 || q[2] == byte 16r45)){ # joliet, it appears
if(dirp != nil)
dirp.put();
dirp = p;
fmt = 'J';
convM2Drec(v[156:], dp, 0); # v.z.desc.rootdir
if(blksize != l16(v[128:])) # v.z.desc.blksize
sys->fprint(stderr, "9660srv: warning: suspicious Joliet block size: %d\n", l16(v[128:]));
chat("joliet...");
continue;
}
}else{
p.put();
if(v[0] == byte 16rFF)
break;
}
}
if(fmt == 0){
if(dirp != nil)
dirp.put();
return "CD format not recognised";
}
if(chatty)
showdrec(stderr, fmt, dp);
if(blksize > Sectorsize){
dirp.put();
return "blocksize too big";
}
fp := iso(root);
root.xf.isplan9 = haveplan9;
fp.fmt = fmt;
fp.blksize = blksize;
fp.offset = 0;
fp.doffset = 0;
fp.d = dp;
root.qid.path = big dp.addr;
root.qid.qtype = QTDIR;
root.qid.vers = 0;
dirp.put();
dp = ref Drec;
if(getdrec(root, dp) >= 0){
s := dp.data;
n := len s;
if(n >= 7 && s[0] == byte 'S' && s[1] == byte 'P' && s[2] == byte 7 &&
s[3] == byte 1 && s[4] == byte 16rBE && s[5] == byte 16rEF){
root.xf.issusp = 1;
root.xf.suspoff = int s[6];
n -= root.xf.suspoff;
s = s[root.xf.suspoff:];
while(n >= 4){
l := int s[2];
if(s[0] == byte 'E' && s[1] == byte 'R'){
if(int s[4] == 10 && eqs(s[8:18], "RRIP_1991A"))
root.xf.isrock = 1;
break;
} else if(s[0] == byte 'C' && s[1] == byte 'E' && int s[2] >= 28){
(s, n) = getcontin(root.xf.d, s);
continue;
} else if(s[0] == byte 'R' && s[1] == byte 'R'){
if(!norock)
root.xf.isrock = 1;
break; # can skip search for ER
} else if(s[0] == byte 'S' && s[1] == byte 'T')
break;
s = s[l:];
n -= l;
}
}
}
if(root.xf.isrock)
chat("Rock Ridge...");
fp.offset = 0;
fp.doffset = 0;
return nil;
}
Xfile.clone(oldf: self ref Xfile, newf: ref Xfile)
{
*newf = *oldf;
newf.ptr = nil;
newf.xf.incref();
ip := iso(oldf);
np := iso(newf);
*np = *ip; # might not be right; shares ip.d
}
Xfile.walkup(f: self ref Xfile): string
{
pf := Xfile.new();
ppf := Xfile.new();
e := walkup(f, pf, ppf);
pf.clunk();
ppf.clunk();
return e;
}
walkup(f, pf, ppf: ref Xfile): string
{
e := opendotdot(f, pf);
if(e != nil)
return sys->sprint("can't open pf: %s", e);
paddr := iso(pf).d.addr;
if(iso(f).d.addr == paddr)
return nil;
e = opendotdot(pf, ppf);
if(e != nil)
return sys->sprint("can't open ppf: %s", e);
d := ref Drec;
while(getdrec(ppf, d) >= 0){
if(d.addr == paddr){
newdrec(f, d);
f.qid.path = big paddr;
f.qid.qtype = QTDIR;
f.qid.vers = 0;
return nil;
}
}
return "can't find addr of ..";
}
Xfile.walk(f: self ref Xfile, name: string): string
{
ip := iso(f);
if(!f.xf.isplan9){
for(i := 0; i < len name; i++)
if(name[i] == ';')
break;
if(i >= Maxname)
i = Maxname-1;
name = name[0:i];
}
if(chatty)
chat(sys->sprint("%d \"%s\"...", len name, name));
ip.offset = 0;
dir := ref Dir;
d := ref Drec;
while(getdrec(f, d) >= 0) {
dvers := rzdir(f.xf, dir, ip.fmt, d);
if(name != dir.name)
continue;
newdrec(f, d);
f.qid.path = dir.qid.path;
f.qid.qtype = dir.qid.qtype;
f.qid.vers = dir.qid.vers;
if(dvers){
# versions ignored
}
return nil;
}
return Enonexist;
}
Xfile.open(f: self ref Xfile, mode: int): string
{
if(mode != Sys->OREAD)
return Eperm;
ip := iso(f);
ip.offset = 0;
ip.doffset = 0;
return nil;
}
Xfile.create(nil: self ref Xfile, nil: string, nil: int, nil: int): string
{
return Eperm;
}
Xfile.readdir(f: self ref Xfile, buf: array of byte, offset: int, count: int): (int, string)
{
ip := iso(f);
d := ref Dir;
drec := ref Drec;
if(offset < ip.doffset){
ip.offset = 0;
ip.doffset = 0;
}
rcnt := 0;
while(rcnt < count && getdrec(f, drec) >= 0){
if(len drec.name == 1){
if(drec.name[0] == byte 0)
continue;
if(drec.name[0] == byte 1)
continue;
}
rzdir(f.xf, d, ip.fmt, drec);
d.qid.vers = f.qid.vers;
a := styx->packdir(*d);
if(ip.doffset < offset){
ip.doffset += len a;
continue;
}
if(rcnt+len a > count)
break;
buf[rcnt:] = a; # BOTCH: copy
rcnt += len a;
}
ip.doffset += rcnt;
return (rcnt, nil);
}
Xfile.read(f: self ref Xfile, buf: array of byte, offset: int, count: int): (int, string)
{
ip := iso(f);
if(offset >= ip.d.size)
return (0, nil);
if(offset+count > ip.d.size)
count = ip.d.size - offset;
addr := (ip.d.addr+ip.d.attrlen)*ip.blksize + offset;
o := addr % Sectorsize;
addr /= Sectorsize;
if(chatty)
chat(sys->sprint("d.addr=0x%x, addr=0x%x, o=0x%x...", ip.d.addr, addr, o));
n := Sectorsize - o;
rcnt := 0;
while(count > 0){
if(n > count)
n = count;
p := Block.get(f.xf.d, addr);
if(p == nil)
return (-1, "i/o error");
buf[rcnt:] = p.data[o:o+n];
p.put();
count -= n;
rcnt += n;
addr++;
o = 0;
n = Sectorsize;
}
return (rcnt, nil);
}
Xfile.write(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string)
{
return (-1, Eperm);
}
Xfile.clunk(f: self ref Xfile)
{
f.ptr = nil;
}
Xfile.remove(nil: self ref Xfile): string
{
return Eperm;
}
Xfile.stat(f: self ref Xfile): (ref Dir, string)
{
ip := iso(f);
d := ref Dir;
rzdir(f.xf, d, ip.fmt, ip.d);
d.qid.vers = f.qid.vers;
if(d.qid.path==f.xf.rootqid.path){
d.qid.path = big 0;
d.qid.qtype = QTDIR;
}
return (d, nil);
}
Xfile.wstat(nil: self ref Xfile, nil: ref Dir): string
{
return Eperm;
}
Xfs.new(d: ref Device): ref Xfs
{
xf := ref Xfs;
xf.inuse = 1;
xf.d = d;
xf.isplan9 = 0;
xf.issusp = 0;
xf.isrock = 0;
xf.suspoff = 0;
xf.ptr = 0;
xf.rootqid = Qid(big 0, 0, QTDIR);
return xf;
}
Xfs.incref(xf: self ref Xfs)
{
xf.inuse++;
}
Xfs.decref(xf: self ref Xfs)
{
xf.inuse--;
if(xf.inuse == 0){
if(xf.d != nil)
xf.d.detach();
}
}
showdrec(fd: ref Sys->FD, fmt: int, d: ref Drec)
{
if(d.reclen == 0)
return;
sys->fprint(fd, "%d %d %d %d ",
d.reclen, d.attrlen, d.addr, d.size);
sys->fprint(fd, "%s 0x%2.2x %d %d %d ",
rdate(d.date, fmt), d.flags,
d.unitsize, d.gapsize, d.vseqno);
sys->fprint(fd, "%d %s", len d.name, nstr(d.name));
syslen := len d.data;
if(syslen != 0)
sys->fprint(fd, " %s", nstr(d.data));
sys->fprint(fd, "\n");
}
newdrec(f: ref Xfile, dp: ref Drec)
{
x := iso(f);
n := ref Isofile;
n.fmt = x.fmt;
n.blksize = x.blksize;
n.offset = 0;
n.doffset = 0;
n.d = dp;
f.ptr = n;
}
getdrec(f: ref Xfile, d: ref Drec): int
{
if(f.ptr == nil)
return -1;
boff := 0;
ip := iso(f);
size := ip.d.size;
while(ip.offset<size){
addr := (ip.d.addr+ip.d.attrlen)*ip.blksize + ip.offset;
boff = addr % Sectorsize;
if(boff > Sectorsize-34){
ip.offset += Sectorsize-boff;
continue;
}
p := Block.get(f.xf.d, addr/Sectorsize);
if(p == nil)
return -1;
nb := int p.data[boff];
if(nb >= 34) {
convM2Drec(p.data[boff:], d, ip.fmt=='r');
#chat(sys->sprint("off %d", ip.offset));
#showdrec(stderr, ip.fmt, d);
p.put();
ip.offset += nb + (nb&1);
return 0;
}
p.put();
p = nil;
ip.offset += Sectorsize-boff;
}
return -1;
}
# getcontin returns a slice of the Iobuf, valid until next i/o call
getcontin(d: ref Device, a: array of byte): (array of byte, int)
{
bn := l32(a[4:]);
off := l32(a[12:]);
n := l32(a[20:]);
p := Block.get(d, bn);
if(p == nil)
return (nil, 0);
return (p.data[off:off+n], n);
}
iso(f: ref Xfile): ref Isofile
{
if(f.ptr == nil){
f.ptr = ref Isofile;
f.ptr.d = ref Drec;
}
return f.ptr;
}
opendotdot(f: ref Xfile, pf: ref Xfile): string
{
d := ref Drec;
ip := iso(f);
ip.offset = 0;
if(getdrec(f, d) < 0)
return "opendotdot: getdrec(.) failed";
if(len d.name != 1 || d.name[0] != byte 0)
return "opendotdot: no . entry";
if(d.addr != ip.d.addr)
return "opendotdot: bad . address";
if(getdrec(f, d) < 0)
return "opendotdot: getdrec(..) failed";
if(len d.name != 1 || d.name[0] != byte 1)
return "opendotdot: no .. entry";
pf.xf = f.xf;
pip := iso(pf);
pip.fmt = ip.fmt;
pip.blksize = ip.blksize;
pip.offset = 0;
pip.doffset = 0;
pip.d = d;
return nil;
}
rzdir(fs: ref Xfs, d: ref Dir, fmt: int, dp: ref Drec): int
{
Hmode, Hname: con 1<<iota;
vers := -1;
have := 0;
d.qid.path = big dp.addr;
d.qid.vers = 0;
d.qid.qtype = QTFILE;
n := len dp.name;
if(n == 1) {
case int dp.name[0] {
0 => d.name = "."; have |= Hname;
1 => d.name = ".."; have |= Hname;
* => d.name = ""; d.name[0] = tolower(int dp.name[0]);
}
} else {
if(fmt == 'J'){ # Joliet, 16-bit Unicode
d.name = "";
for(i:=0; i<n; i+=2){
r := (int dp.name[i]<<8) | int dp.name[i+1];
d.name[len d.name] = r;
}
}else{
if(n >= Maxname)
n = Maxname-1;
d.name = "";
for(i:=0; i<n && int dp.name[i] != '\r'; i++)
d.name[i] = tolower(int dp.name[i]);
}
}
if(fs.isplan9 && dp.reclen>34+len dp.name) {
#
# get gid, uid, mode and possibly name
# from plan9 directory extension
#
s := dp.data;
n = int s[0];
if(n)
d.name = string s[1:1+n];
l := 1+n;
n = int s[l++];
d.uid = string s[l:l+n];
l += n;
n = int s[l++];
d.gid = string s[l:l+n];
l += n;
if(l & 1)
l++;
d.mode = l32(s[l:]);
if(d.mode & DMDIR)
d.qid.qtype = QTDIR;
} else {
d.mode = 8r444;
case fmt {
'z' =>
if(fs.isrock)
d.gid = "ridge";
else
d.gid = "iso";
'r' =>
d.gid = "sierra";
'J' =>
d.gid = "joliet";
* =>
d.gid = "???";
}
flags := dp.flags;
if(flags & 2){
d.qid.qtype = QTDIR;
d.mode |= DMDIR|8r111;
}
d.uid = "cdrom";
for(i := 0; i < len d.name; i++)
if(d.name[i] == ';') {
vers = int string d.name[i+1:]; # inefficient
d.name = d.name[0:i]; # inefficient
break;
}
n = len dp.data - fs.suspoff;
if(fs.isrock && n >= 4){
s := dp.data[fs.suspoff:];
nm := 0;
while(n >= 4 && have != (Hname|Hmode)){
l := int s[2];
if(s[0] == byte 'P' && s[1] == byte 'X' && s[3] == byte 1){
# posix file attributes
mode := l32(s[4:12]);
d.mode = mode & 8r777;
if((mode & 8r170000) == 8r0040000){
d.mode |= DMDIR;
d.qid.qtype = QTDIR;
}
have |= Hmode;
} else if(s[0] == byte 'N' && s[1] == byte 'M' && s[3] == byte 1){
# alternative name
flags = int s[4];
if((flags & ~1) == 0){
if(nm == 0){
d.name = string s[5:l];
nm = 1;
} else
d.name += string s[5:l];
if(flags == 0)
have |= Hname; # no more
}
} else if(s[0] == byte 'C' && s[1] == byte 'E' && int s[2] >= 28){
(s, n) = getcontin(fs.d, s);
continue;
} else if(s[0] == byte 'S' && s[1] == byte 'T')
break;
n -= l;
s = s[l:];
}
}
}
d.length = big 0;
if((d.mode & DMDIR) == 0)
d.length = big dp.size;
d.dtype = 0;
d.dev = 0;
d.atime = dp.time;
d.mtime = d.atime;
return vers;
}
convM2Drec(a: array of byte, d: ref Drec, highsierra: int)
{
d.reclen = int a[0];
d.attrlen = int a[1];
d.addr = int l32(a[2:10]);
d.size = int l32(a[10:18]);
d.time = gtime(a[18:24]);
d.date = array[7] of byte;
d.date[0:] = a[18:25];
if(highsierra){
d.tzone = 0;
d.flags = int a[24];
d.unitsize = 0;
d.gapsize = 0;
d.vseqno = 0;
} else {
d.tzone = int a[24];
d.flags = int a[25];
d.unitsize = int a[26];
d.gapsize = int a[27];
d.vseqno = l32(a[28:32]);
}
n := int a[32];
d.name = array[n] of byte;
d.name[0:] = a[33:33+n];
n += 33;
if(n & 1)
n++; # check this
syslen := d.reclen - n;
if(syslen > 0){
d.data = array[syslen] of byte;
d.data[0:] = a[n:n+syslen];
} else
d.data = nil;
}
nstr(p: array of byte): string
{
q := "";
n := len p;
for(i := 0; i < n; i++){
if(int p[i] == '\\')
q[len q] = '\\';
if(' ' <= int p[i] && int p[i] <= '~')
q[len q] = int p[i];
else
q += sys->sprint("\\%2.2ux", int p[i]);
}
return q;
}
rdate(p: array of byte, fmt: int): string
{
c: int;
s := sys->sprint("%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
int p[0], int p[1], int p[2], int p[3], int p[4], int p[5]);
if(fmt == 'z'){
htz := int p[6];
if(htz >= 128){
htz = 256-htz;
c = '-';
}else
c = '+';
s += sys->sprint(" (%c%.1f)", c, real htz/2.0);
}
return s;
}
dmsize := array[] of {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
dysize(y: int): int
{
if((y%4) == 0)
return 366;
return 365;
}
gtime(p: array of byte): int # yMdhms
{
y:=int p[0]; M:=int p[1]; d:=int p[2];
h:=int p[3]; m:=int p[4]; s:=int p[5];;
if(y < 70)
return 0;
if(M < 1 || M > 12)
return 0;
if(d < 1 || d > dmsize[M-1])
return 0;
if(h > 23)
return 0;
if(m > 59)
return 0;
if(s > 59)
return 0;
y += 1900;
t := 0;
for(i:=1970; i<y; i++)
t += dysize(i);
if(dysize(y)==366 && M >= 3)
t++;
M--;
while(M-- > 0)
t += dmsize[M];
t += d-1;
t = 24*t + h;
t = 60*t + m;
t = 60*t + s;
return t;
}
l16(p: array of byte): int
{
v := (int p[1]<<8)| int p[0];
if (v >= 16r8000)
v -= 16r10000;
return v;
}
l32(p: array of byte): int
{
return (((((int p[3]<<8)| int p[2])<<8)| int p[1])<<8)| int p[0];
}
eqs(a: array of byte, b: string): int
{
if(len a != len b)
return 0;
for(i := 0; i < len a; i++)
if(int a[i] != b[i])
return 0;
return 1;
}
tolower(c: int): int
{
if(c >= 'A' && c <= 'Z')
return c-'A' + 'a';
return c;
}
#
# I/O buffers
#
Device: adt {
inuse: int; # attach count
name: string; # of underlying file
fd: ref Sys->FD;
sectorsize: int;
qid: Sys->Qid; # (qid,dtype,dev) identify uniquely
dtype: int;
dev: int;
detach: fn(nil: self ref Device);
};
Block: adt {
dev: ref Device;
addr: int;
data: array of byte;
# internal
next: cyclic ref Block;
prev: cyclic ref Block;
busy: int;
get: fn(nil: ref Device, addr: int): ref Block;
put: fn(nil: self ref Block);
};
devices: list of ref Device;
NIOB: con 100; # for starters
HIOB: con 127; # prime
hiob := array[HIOB] of list of ref Block; # hash buckets
iohead: ref Block;
iotail: ref Block;
bufsize := 0;
iobufinit(bsize: int)
{
bufsize = bsize;
for(i:=0; i<NIOB; i++)
newblock();
}
newblock(): ref Block
{
p := ref Block;
p.busy = 0;
p.addr = -1;
p.dev = nil;
p.data = array[bufsize] of byte;
p.next = iohead;
if(iohead != nil)
iohead.prev = p;
iohead = p;
if(iotail == nil)
iotail = p;
return p;
}
Block.get(dev: ref Device, addr: int): ref Block
{
p: ref Block;
dh := hiob[addr%HIOB:];
for(l := dh[0]; l != nil; l = tl l) {
p = hd l;
if(p.addr == addr && p.dev == dev) {
p.busy++;
return p;
}
}
# Find a non-busy buffer from the tail
for(p = iotail; p != nil && p.busy; p = p.prev)
;
if(p == nil)
p = newblock();
# Delete from hash chain
if(p.addr >= 0) {
hp := hiob[p.addr%HIOB:];
l = nil;
for(f := hp[0]; f != nil; f = tl f)
if(hd f != p)
l = (hd f) :: l;
hp[0] = l;
}
# Hash and fill
p.addr = addr;
p.dev = dev;
p.busy++;
sys->seek(dev.fd, big addr*big dev.sectorsize, 0);
if(sys->read(dev.fd, p.data, dev.sectorsize) != dev.sectorsize){
p.addr = -1; # stop caching
p.put();
purge(dev);
return nil;
}
dh[0] = p :: dh[0];
return p;
}
Block.put(p: self ref Block)
{
p.busy--;
if(p.busy < 0)
panic("Block.put");
if(p == iohead)
return;
# Link onto head for lru
if(p.prev != nil)
p.prev.next = p.next;
else
iohead = p.next;
if(p.next != nil)
p.next.prev = p.prev;
else
iotail = p.prev;
p.prev = nil;
p.next = iohead;
iohead.prev = p;
iohead = p;
}
purge(dev: ref Device)
{
for(i := 0; i < HIOB; i++){
l := hiob[i];
hiob[i] = nil;
for(; l != nil; l = tl l){ # reverses bucket's list, but never mind
p := hd l;
if(p.dev == dev)
p.busy = 0;
else
hiob[i] = p :: hiob[i];
}
}
}
devattach(name: string, mode: int, sectorsize: int): (ref Device, string)
{
if(sectorsize > bufsize)
return (nil, "sector size too big");
fd := sys->open(name, mode);
if(fd == nil)
return(nil, sys->sprint("%s: can't open: %r", name));
(rc, dir) := sys->fstat(fd);
if(rc < 0)
return (nil, sys->sprint("%r"));
for(dl := devices; dl != nil; dl = tl dl){
d := hd dl;
if(d.qid.path != dir.qid.path || d.qid.vers != dir.qid.vers)
continue;
if(d.dtype != dir.dtype || d.dev != dir.dev)
continue;
d.inuse++;
if(chatty)
sys->print("inuse=%d, \"%s\", dev=%H...\n", d.inuse, d.name, d.fd);
return (d, nil);
}
if(chatty)
sys->print("alloc \"%s\", dev=%H...\n", name, fd);
d := ref Device;
d.inuse = 1;
d.name = name;
d.qid = dir.qid;
d.dtype = dir.dtype;
d.dev = dir.dev;
d.fd = fd;
d.sectorsize = sectorsize;
devices = d :: devices;
return (d, nil);
}
Device.detach(d: self ref Device)
{
d.inuse--;
if(d.inuse < 0)
panic("putxdata");
if(chatty)
sys->print("decref=%d, \"%s\", dev=%H...\n", d.inuse, d.name, d.fd);
if(d.inuse == 0){
if(chatty)
sys->print("purge...\n");
purge(d);
dl := devices;
devices = nil;
for(; dl != nil; dl = tl dl)
if((hd dl) != d)
devices = (hd dl) :: devices;
}
}
panic(s: string)
{
sys->print("panic: %s\n", s);
a: array of byte;
a[5] = byte 0; # trap
}