implement Muxdat;
include "sys.m";
sys: Sys;
fildes, fprint, FD, OTRUNC, DMDIR, ORCLOSE, OREAD, ORDWR,
Qid, print, pctl, create, mount, QTDIR, pread, open, sprint,
pwrite, remove, nulldir, fwstat, wstat, fstat, stat, Dir, write, sleep, millisec: import sys;
include "draw.m";
include "error.m";
err: Error;
checkload, stderr, panic, kill, error: import err;
include "query.m";
query: Query;
include "hash.m";
hash: Hash;
HashVal, HashNode, HashTable: import hash;
include "names.m";
names: Names;
dirname, cleanname, relative, isprefix, rooted: import names;
include "muxdat.m";
include "tbl.m";
tbl: Tbl;
Table: import tbl;
attrs: list of string;
qids: ref HashTable; # qid.path table, indexed by file name
fids: ref Table[ref Fid]; # fid table, indexed by qid.
metaattrs := 0; # uses $user in attrs
last := 0;
member(l: list of string, s: string): int
{
for(; l != nil; l = tl l)
if (hd l == s)
return 1;
return 0;
}
init(s: Sys, e: Error, n: Names, args: list of string)
{
sys = s; err = e; names = n;
query = checkload(load Query Query->PATH, Query->PATH);
hash = checkload(load Hash Hash->PATH, Hash->PATH);
tbl = checkload(load Tbl Tbl->PATH, Tbl->PATH);
rootdir = Empty;
nullfid: ref Fid;
fids = Table[ref Fid].new(103, nullfid);
qids = hash->new(103); # use a prime number
attrs = args;
if (member(attrs, "$user"))
metaattrs = 1;
last = millisec();
}
bindrootdir()
{
(fspaths, e) := query->lookup(attrs);
if (debug && e != nil)
fprint(stderr, "mux: query: %s\n", e);
while(fspaths != nil){
rootdir = hd fspaths;
(se, nil) := stat(rootdir);
if (se >= 0)
break;
if (debug)
fprint(stderr, "mux: binding %s: %r\n", rootdir);
fspaths = tl fspaths;
}
if (fspaths == nil)
rootdir = Empty;
if (debug)
fprint(stderr, "mux: rootdir bound to %s\n", rootdir);
}
rebindfids(old, new: string, brk: int)
{
for (i := 0; i < len fids.items; i++){
for (l := fids.items[i]; l != nil; l = tl l){
(nil, fid) := hd l;
wasbroken := fid.broken;
if (fid.fd != nil) # could maintain for OREAD fids, perhaps.
fid.broken |= brk;
if (fid.broken){
if (debug && !wasbroken)
fprint(stderr, "rebindfids: broken fid %s\n", fid.path);
continue;
}
if (isprefix(old, fid.path) || fid.path == old){
opath := fid.path;
rel := relative(fid.path, old);
fid.path = rooted(new, rel);
if (brk)
fid.qid.vers++; # force a change; least thing we could do.
if (debug)
fprint(stderr, "rebindfid: %s\t->\t%s\n", opath, fid.path);
}
}
}
}
rebindqids(old, new: string)
{
for(paths := qids.all(); paths != nil; paths = tl paths){
nd := hd paths;
p := nd.key;
v := nd.val;
if (isprefix(old, p) || old == p){
opath := p;
rel := relative(p, old);
npath := rooted(new, rel);
qids.insert(npath, *v);
qids.delete(p);
if (debug)
fprint(stderr, "rebindqid: %s\t->\t%s\n", opath, npath);
}
}
}
maybebroken(nil: string)
{
if (brokenfs != 0)
return;
# if (estr contains "i/o error")
# brokenfs = 1;
# else if (estr contains "clone failed")
# brokenfs = 1;
# else if (estr contains "hangup")
# brokenfs = 1;
# else {
(e, nil) := stat(rootdir);
if (e < 0)
brokenfs = 1;
# }
}
rootdirmatches(): int
{
(fspaths, e) := query->lookup(attrs);
if (debug && e != nil)
fprint(stderr, "mux: query: %s\n", e);
while(fspaths != nil){
if (rootdir == hd fspaths)
return 1;
fspaths = tl fspaths;
}
return 0;
}
rebind()
{
if (rootdir != Empty && !brokenfs && !metaattrs)
return;
old := rootdir;
if (brokenfs || rootdir == Empty)
bindrootdir();
else {
now := millisec();
if (now - last < 60 * 1000)
return;
last = now;
if (!rootdirmatches())
bindrootdir();
}
if (rootdir == Empty && old == rootdir) # nothing can be done
return;
# update our pahts, and break fids already open
rebindfids(old, rootdir, 1);
rebindqids(old, rootdir);
brokenfs = 0;
}
renametree(old: string, nname: string)
{
odir := dirname(old);
new := rooted(odir, nname);
if (debug)
fprint(stderr, "renametree: from %s to %s\n", old, new);
rebindfids(old, new, 0);
rebindqids(old, new);
}
addfid(fid: ref Fid): int
{
return fids.add(fid.fid, fid);
}
delfid(fid: ref Fid)
{
fids.del(fid.fid);
}
getfid(f: int): ref Fid
{
return fids.find(f);
}
addqid(path: string): big
{
q := qgen++;
qids.insert(path, HashVal(q, 0.0, nil));
return big q;
}
getqid(path: string): big
{
v := qids.find(path);
if (v == nil)
return NOQID;
return big v.i;
}
delqid(path: string)
{
qids.delete(path);
}
# We must ensure that when a qid changes in the server,
# at least the version changes for the client. Otherwise,
# very bad things may hapen if, for example, the client
# is using the qids to build a cache of binary file images.
# This is taken straight from Plan B's bns.
fixqid(path: string, sq: Qid): Qid
{
q := getqid(path);
if (q == NOQID)
q = addqid(path);
sq.vers = ((int sq.path)^sq.vers);
sq.path = q;
return sq;
}
|