#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include <fcall.h>
#include "object.h"
extern int debug;
extern int mfd[];
enum {
DbgFs = 0x1000
};
typedef struct Fid Fid;
enum {
Busy = 0x01,
Open = 0x02,
Endf = 0x04,
};
struct Fid
{
QLock;
Qid qid;
int fid;
ushort flags;
vlong offset; // offset of data[0]
Fid *next;
};
Fcall thdr;
Fcall rhdr;
enum {
/* Files making up an object */
Qchildren, /* Each of these must be in dirtab */
Qdigest,
Qfiles,
Qfulltext,
Qkey,
Qminiparentage,
Qparent,
Qparentage,
Qtext,
Qtype,
/* Other files */
Qtop, /* Must follow Qtype */
Qclassical,
Qdir,
Qroot,
Qctl,
};
#define PATH(id, f) (((id)<<8) | (f))
#define FILE(p) ((p) & 0xff)
#define NUM(p) ((p) >> 8)
char *dirtab[] =
{
[Qchildren] "children",
[Qdigest] "digest",
[Qdir] ".",
[Qfiles] "files",
[Qfulltext] "fulltext",
[Qkey] "key",
[Qminiparentage]"miniparentage",
[Qparent] "parent",
[Qparentage] "parentage",
[Qtext] "text",
[Qtype] "type",
[Qtop] nil,
};
char *rflush(Fid*), *rauth(Fid*),
*rattach(Fid*), *rwalk(Fid*),
*ropen(Fid*), *rcreate(Fid*),
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
*rversion(Fid*);
char *(*fcalls[])(Fid*) = {
[Tflush] rflush,
[Tversion] rversion,
[Tauth] rauth,
[Tattach] rattach,
[Twalk] rwalk,
[Topen] ropen,
[Tcreate] rcreate,
[Tread] rread,
[Twrite] rwrite,
[Tclunk] rclunk,
[Tremove] rremove,
[Tstat] rstat,
[Twstat] rwstat,
};
int messagesize = 8*1024+IOHDRSZ;
uchar mdata[8*1024+IOHDRSZ];
uchar mbuf[8*1024+IOHDRSZ];
char bigbuf[1<<23]; /* 8 megabytes */
Fid *fids;
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enoauth[] = "no authentication required";
char Enotexist[] = "file does not exist";
char Einuse[] = "file in use";
char Eexist[] = "file exists";
char Enotowner[] = "not owner";
char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
char Ebadctl[] = "unknown control message";
Fid *newfid(int fid);
static int
lookup(char *cmd, char *list[])
{
int i;
for (i = 0; list[i] != nil; i++)
if (strcmp(cmd, list[i]) == 0)
return i;
return -1;
}
char*
rversion(Fid *)
{
Fid *f;
if(thdr.msize < 256)
return "max messagesize too small";
if(thdr.msize < messagesize)
messagesize = thdr.msize;
rhdr.msize = messagesize;
if(strncmp(thdr.version, "9P2000", 6) != 0)
return "unknown 9P version";
else
rhdr.version = "9P2000";
for(f = fids; f; f = f->next)
if(f->flags & Busy)
rclunk(f);
return nil;
}
char*
rauth(Fid*)
{
return Enoauth;
}
char*
rflush(Fid *)
{
return 0;
}
char*
rattach(Fid *f)
{
f->flags |= Busy;
f->qid.type = QTDIR;
f->qid.vers = 0;
f->qid.path = PATH(0, Qtop);
rhdr.qid = f->qid;
return 0;
}
static Fid*
doclone(Fid *f, int nfid)
{
Fid *nf;
nf = newfid(nfid);
nf->qid = f->qid;
if(nf->flags & Busy)
return nil;
nf->flags |= Busy;
nf->flags &= ~Open;
return nf;
}
char*
dowalk(Fid *f, char *name)
{
int t, n, m;
char *rv, *p;
t = FILE(f->qid.path); /* Type */
rv = Enotexist;
if(strcmp(name, ".") == 0 && f->qid.type == QTDIR)
return nil;
if(strcmp(name, "..") == 0){
switch(t){
case Qtop:
case Qclassical:
f->qid.path = PATH(0, Qtop);
f->qid.type = QTDIR;
f->qid.vers = 0;
rv = nil;
break;
case Qdir:
case Qroot:
f->qid.path = PATH(0, Qclassical);
f->qid.type = QTDIR;
f->qid.vers = 0;
rv = nil;
break;
}
return rv;
}
switch(t){
case Qtop:
/* Contains classical */
if(strcmp(name, "juke") == 0){
f->qid.path = PATH(root->tabno, Qclassical);
f->qid.type = QTDIR;
f->qid.vers = 0;
rv = nil;
break;
}
break;
case Qclassical:
/* main dir, contains `root' and object dirs */
if(strcmp(name, "root") == 0){
f->qid.path = PATH(root->tabno, Qroot);
f->qid.type = QTDIR;
f->qid.vers = 0;
rv = nil;
break;
}
if(strcmp(name, "ctl") == 0){
f->qid.path = PATH(root->tabno, Qctl);
f->qid.type = QTFILE;
f->qid.vers = 0;
rv = nil;
break;
}
n = strtol(name, &p, 0);
if(*p)
break; /* Not a number */
if(n < 0 || n >= notab)
break; /* Outside range */
if(otab[n] == nil)
break; /* Not in object table */
f->qid.path = PATH(n, Qdir);
f->qid.type = QTDIR;
f->qid.vers = 0;
rv = nil;
break;
case Qroot: /* Root of the object hierarchy */
case Qdir: /* Object directory */
if((m = lookup(name, dirtab)) < 0)
break;
n = NUM(f->qid.path);
f->qid.path = PATH(n, m);
f->qid.type = QTFILE;
f->qid.vers = 0;
rv = nil;
break;
}
return rv;
}
char*
rwalk(Fid *f)
{
Fid *nf;
char *rv;
int i;
if(f->flags & Open)
return Eisopen;
rhdr.nwqid = 0;
nf = nil;
/* clone if requested */
if(thdr.newfid != thdr.fid){
nf = doclone(f, thdr.newfid);
if(nf == nil)
return "new fid in use";
f = nf;
}
/* if it's just a clone, return */
if(thdr.nwname == 0 && nf != nil)
return nil;
/* walk each element */
rv = nil;
for(i = 0; i < thdr.nwname; i++){
rv = dowalk(f, thdr.wname[i]);
if(rv != nil){
if(nf != nil)
rclunk(nf);
break;
}
rhdr.wqid[i] = f->qid;
}
rhdr.nwqid = i;
/* we only error out if no walk */
if(i > 0)
rv = nil;
return rv;
}
char *
ropen(Fid *f)
{
if(f->flags & Open)
return Eisopen;
if(thdr.mode != OREAD && FILE(f->qid.path) != Qctl)
return Eperm;
rhdr.iounit = 0;
rhdr.qid = f->qid;
f->flags |= Open;
f->flags &= ~Endf;
return nil;
}
char *
rcreate(Fid*)
{
return Eperm;
}
static long
fileinfo(char *buf, int bufsize, int onum, int t)
{
long n;
n = 0;
switch(t){
case Qchildren:
n = printchildren(buf, bufsize, otab[onum]);
break;
case Qdigest:
n = printdigest(buf, bufsize, otab[onum]);
break;
case Qfulltext:
n = printfulltext(buf, bufsize, otab[onum]);
break;
case Qkey:
n = printkey(buf, bufsize, otab[onum]);
break;
case Qparent:
n = printparent(buf, bufsize, otab[onum]);
break;
case Qtext:
n = printtext(buf, bufsize, otab[onum]);
break;
case Qtype:
n = printtype(buf, bufsize, otab[onum]);
break;
case Qfiles:
n = printfiles(buf, bufsize, otab[onum]);
break;
case Qparentage:
n = printparentage(buf, bufsize, otab[onum]);
break;
case Qminiparentage:
n = printminiparentage(buf, bufsize, otab[onum]);
break;
default:
sysfatal("rread: %d", t);
}
return n;
}
static void
mkstat(Dir *d, int n, int t)
{
static char buf[16];
d->uid = user;
d->gid = user;
d->muid = user;
d->qid.vers = 0;
d->qid.type = QTFILE;
d->type = 0;
d->dev = 0;
d->atime = time(0);
d->mtime = d->atime;
switch(t){
case Qtop:
d->name = ".";
d->mode = DMDIR|0555;
d->atime = d->mtime = time(0);
d->length = 0;
d->qid.path = PATH(0, Qtop);
d->qid.type = QTDIR;
break;
case Qclassical:
d->name = "juke";
d->mode = DMDIR|0555;
d->atime = d->mtime = time(0);
d->length = 0;
d->qid.path = PATH(0, Qclassical);
d->qid.type = QTDIR;
break;
case Qdir:
snprint(buf, sizeof buf, "%d", n);
d->name = buf;
d->mode = DMDIR|0555;
d->length = 0;
d->qid.path = PATH(n, Qdir);
d->qid.type = QTDIR;
break;
case Qroot:
d->name = "root";
d->mode = DMDIR|0555;
d->length = 0;
d->qid.path = PATH(0, Qroot);
d->qid.type = QTDIR;
break;
case Qctl:
d->name = "ctl";
d->mode = 0666;
d->length = 0;
d->qid.path = PATH(0, Qctl);
break;
d->name = "ctl";
d->mode = 0666;
d->length = 0;
d->qid.path = PATH(0, Qctl);
break;
case Qchildren:
case Qdigest:
case Qfiles:
case Qfulltext:
case Qkey:
case Qminiparentage:
case Qparent:
case Qparentage:
case Qtext:
case Qtype:
d->name = dirtab[t];
d->mode = 0444;
d->length = fileinfo(bigbuf, sizeof bigbuf, n, t);
d->qid.path = PATH(n, t);
break;
default:
sysfatal("mkstat: %d", t);
}
}
int
readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
{
int m, n;
Dir d;
n = 0;
mkstat(&d, 0, Qclassical);
m = convD2M(&d, &buf[n], blen);
if(off <= 0){
if(m <= BIT16SZ || m > cnt)
return n;
n += m;
}
return n;
}
int
readclasdir(Fid*, uchar *buf, long off, int cnt, int blen)
{
int m, n;
long pos;
Dir d;
Fid *fid;
n = 0;
pos = 0;
mkstat(&d, 0, Qctl);
m = convD2M(&d, &buf[n], blen);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
return 0;
n += m;
cnt -= m;
}
pos += m;
mkstat(&d, 0, Qroot);
m = convD2M(&d, &buf[n], blen);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
return n;
n += m;
cnt -= m;
}
pos += m;
for (fid = fids; fid; fid = fid->next){
if(FILE(fid->qid.path) != Qdir)
continue;
mkstat(&d, NUM(fid->qid.path), Qdir);
m = convD2M(&d, &buf[n], blen-n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
break;
n += m;
cnt -= m;
}
pos += m;
}
return n;
}
int
readdir(Fid *f, uchar *buf, long off, int cnt, int blen)
{
int i, m, n;
long pos;
Dir d;
n = 0;
pos = 0;
for (i = 0; i < Qtop; i++){
mkstat(&d, NUM(f->qid.path), i);
m = convD2M(&d, &buf[n], blen-n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
break;
n += m;
cnt -= m;
}
pos += m;
}
return n;
}
void
readbuf(char *s, long n)
{
rhdr.count = thdr.count;
if(thdr.offset >= n){
rhdr.count = 0;
return;
}
if(thdr.offset+rhdr.count > n)
rhdr.count = n - thdr.offset;
rhdr.data = s + thdr.offset;
}
char*
rread(Fid *f)
{
long off;
int n, cnt, t;
rhdr.count = 0;
off = thdr.offset;
cnt = thdr.count;
if(cnt > messagesize - IOHDRSZ)
cnt = messagesize - IOHDRSZ;
rhdr.data = (char*)mbuf;
n = 0;
t = FILE(f->qid.path);
switch(t){
case Qtop:
n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
rhdr.count = n;
return nil;
case Qclassical:
n = readclasdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
rhdr.count = n;
return nil;
case Qdir:
case Qroot:
n = readdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
rhdr.count = n;
return nil;
case Qctl:
snprint(bigbuf, sizeof bigbuf, "%d objects in tree (%d holes)\n", notab, hotab);
break;
case Qchildren:
case Qdigest:
case Qfiles:
case Qfulltext:
case Qkey:
case Qminiparentage:
case Qparent:
case Qparentage:
case Qtext:
case Qtype:
n = fileinfo(bigbuf, sizeof bigbuf, NUM(f->qid.path), t);
break;
default:
sysfatal("rread: %d", t);
}
readbuf(bigbuf, n);
return nil;
}
char*
rwrite(Fid *f)
{
long cnt;
char *p;
if(FILE(f->qid.path) != Qctl)
return Eperm;
rhdr.count = 0;
cnt = thdr.count;
if(p = strchr(thdr.data, '\n'))
*p = '\0';
if(strncmp(thdr.data, "quit", cnt) == 0)
threadexitsall(nil);
else if(strncmp(thdr.data, "reread", cnt) == 0)
reread();
else
return "illegal command";
rhdr.count = thdr.count;
return nil;
}
char *
rclunk(Fid *f)
{
f->flags &= ~(Open|Busy);
return 0;
}
char *
rremove(Fid *)
{
return Eperm;
}
char *
rstat(Fid *f)
{
Dir d;
mkstat(&d, NUM(f->qid.path), FILE(f->qid.path));
rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
rhdr.stat = mbuf;
return 0;
}
char *
rwstat(Fid*)
{
return Eperm;
}
Fid *
newfid(int fid)
{
Fid *f, *ff;
ff = nil;
for(f = fids; f; f = f->next)
if(f->fid == fid){
return f;
}else if(ff == nil && (f->flags & Busy) == 0)
ff = f;
if(ff == nil){
ff = malloc(sizeof *ff);
if (ff == nil)
sysfatal("malloc: %r");
memset(ff, 0, sizeof *ff);
ff->next = fids;
fids = ff;
}
ff->fid = fid;
return ff;
}
void
io(void *)
{
char *err, e[32];
int n;
extern int p[];
Fid *f;
threadsetname("file server");
close(p[1]);
for(;;){
/*
* reading from a pipe or a network device
* will give an error after a few eof reads
* however, we cannot tell the difference
* between a zero-length read and an interrupt
* on the processes writing to us,
* so we wait for the error
*/
n = read9pmsg(mfd[0], mdata, messagesize);
if(n == 0)
continue;
if(n < 0){
rerrstr(e, sizeof e);
if (strcmp(e, "interrupted") == 0){
if (debug & DbgFs) fprint(2, "read9pmsg interrupted\n");
continue;
}
return;
}
if(convM2S(mdata, n, &thdr) == 0)
continue;
if(debug & DbgFs)
fprint(2, "io:<-%F\n", &thdr);
rhdr.data = (char*)mbuf;
if(!fcalls[thdr.type])
err = "bad fcall type";
else {
f = newfid(thdr.fid);
err = (*fcalls[thdr.type])(f);
}
if(err){
rhdr.type = Rerror;
rhdr.ename = err;
}else{
rhdr.type = thdr.type + 1;
rhdr.fid = thdr.fid;
}
rhdr.tag = thdr.tag;
if(debug & DbgFs)
fprint(2, "io:->%F\n", &rhdr);/**/
n = convS2M(&rhdr, mdata, messagesize);
if(write(mfd[1], mdata, n) != n)
sysfatal("mount write");
}
threadexitsall("die yankee pig dog");
}
int
newid(void)
{
int rv;
static int id;
static Lock idlock;
lock(&idlock);
rv = ++id;
unlock(&idlock);
return rv;
}
|