|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <draw.h> | 
|  | #include <thread.h> | 
|  | #include <cursor.h> | 
|  | #include <mouse.h> | 
|  | #include <keyboard.h> | 
|  | #include <frame.h> | 
|  | #include <fcall.h> | 
|  | #include <plumb.h> | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  |  | 
|  | static	int	sfd; | 
|  |  | 
|  | enum | 
|  | { | 
|  | Nhash	= 16, | 
|  | DEBUG	= 0 | 
|  | }; | 
|  |  | 
|  | static	Fid	*fids[Nhash]; | 
|  |  | 
|  | Fid	*newfid(int); | 
|  |  | 
|  | static	Xfid*	fsysflush(Xfid*, Fid*); | 
|  | static	Xfid*	fsysauth(Xfid*, Fid*); | 
|  | static	Xfid*	fsysversion(Xfid*, Fid*); | 
|  | static	Xfid*	fsysattach(Xfid*, Fid*); | 
|  | static	Xfid*	fsyswalk(Xfid*, Fid*); | 
|  | static	Xfid*	fsysopen(Xfid*, Fid*); | 
|  | static	Xfid*	fsyscreate(Xfid*, Fid*); | 
|  | static	Xfid*	fsysread(Xfid*, Fid*); | 
|  | static	Xfid*	fsyswrite(Xfid*, Fid*); | 
|  | static	Xfid*	fsysclunk(Xfid*, Fid*); | 
|  | static	Xfid*	fsysremove(Xfid*, Fid*); | 
|  | static	Xfid*	fsysstat(Xfid*, Fid*); | 
|  | static	Xfid*	fsyswstat(Xfid*, Fid*); | 
|  |  | 
|  | Xfid* 	(*fcall[Tmax])(Xfid*, Fid*); | 
|  |  | 
|  | static void | 
|  | initfcall(void) | 
|  | { | 
|  | fcall[Tflush]	= fsysflush; | 
|  | fcall[Tversion]	= fsysversion; | 
|  | fcall[Tauth]	= fsysauth; | 
|  | fcall[Tattach]	= fsysattach; | 
|  | fcall[Twalk]	= fsyswalk; | 
|  | fcall[Topen]	= fsysopen; | 
|  | fcall[Tcreate]	= fsyscreate; | 
|  | fcall[Tread]	= fsysread; | 
|  | fcall[Twrite]	= fsyswrite; | 
|  | fcall[Tclunk]	= fsysclunk; | 
|  | fcall[Tremove]= fsysremove; | 
|  | fcall[Tstat]	= fsysstat; | 
|  | fcall[Twstat]	= fsyswstat; | 
|  | } | 
|  |  | 
|  | char Eperm[] = "permission denied"; | 
|  | char Eexist[] = "file does not exist"; | 
|  | char Enotdir[] = "not a directory"; | 
|  |  | 
|  | Dirtab dirtab[]= | 
|  | { | 
|  | { ".",			QTDIR,	Qdir,		0500|DMDIR }, | 
|  | { "acme",		QTDIR,	Qacme,	0500|DMDIR }, | 
|  | { "cons",		QTFILE,	Qcons,	0600 }, | 
|  | { "consctl",	QTFILE,	Qconsctl,	0000 }, | 
|  | { "draw",		QTDIR,	Qdraw,	0000|DMDIR },	/* to suppress graphics progs started in acme */ | 
|  | { "editout",	QTFILE,	Qeditout,	0200 }, | 
|  | { "index",		QTFILE,	Qindex,	0400 }, | 
|  | { "label",		QTFILE,	Qlabel,	0600 }, | 
|  | { "new",		QTDIR,	Qnew,	0500|DMDIR }, | 
|  | { nil, } | 
|  | }; | 
|  |  | 
|  | Dirtab dirtabw[]= | 
|  | { | 
|  | { ".",			QTDIR,		Qdir,			0500|DMDIR }, | 
|  | { "addr",		QTFILE,		QWaddr,		0600 }, | 
|  | { "body",		QTAPPEND,	QWbody,		0600|DMAPPEND }, | 
|  | { "ctl",		QTFILE,		QWctl,		0600 }, | 
|  | { "data",		QTFILE,		QWdata,		0600 }, | 
|  | { "editout",	QTFILE,		QWeditout,	0200 }, | 
|  | { "errors",		QTFILE,		QWerrors,		0200 }, | 
|  | { "event",		QTFILE,		QWevent,		0600 }, | 
|  | { "rdsel",		QTFILE,		QWrdsel,		0400 }, | 
|  | { "wrsel",		QTFILE,		QWwrsel,		0200 }, | 
|  | { "tag",		QTAPPEND,	QWtag,		0600|DMAPPEND }, | 
|  | { "xdata",		QTFILE,		QWxdata,		0600 }, | 
|  | { nil, } | 
|  | }; | 
|  |  | 
|  | typedef struct Mnt Mnt; | 
|  | struct Mnt | 
|  | { | 
|  | QLock	lk; | 
|  | int		id; | 
|  | Mntdir	*md; | 
|  | }; | 
|  |  | 
|  | Mnt	mnt; | 
|  |  | 
|  | Xfid*	respond(Xfid*, Fcall*, char*); | 
|  | int		dostat(int, Dirtab*, uchar*, int, uint); | 
|  | uint	getclock(void); | 
|  |  | 
|  | char	*user = "Wile E. Coyote"; | 
|  | static int closing = 0; | 
|  | int	messagesize = Maxblock+IOHDRSZ;	/* good start */ | 
|  |  | 
|  | void	fsysproc(void *); | 
|  |  | 
|  | void | 
|  | fsysinit(void) | 
|  | { | 
|  | int p[2]; | 
|  | char *u; | 
|  |  | 
|  | initfcall(); | 
|  | if(pipe(p) < 0) | 
|  | error("can't create pipe"); | 
|  | if(post9pservice(p[0], "acme") < 0) | 
|  | error("can't post service"); | 
|  | sfd = p[1]; | 
|  | fmtinstall('F', fcallfmt); | 
|  | if((u = getuser()) != nil) | 
|  | user = estrdup(u); | 
|  | proccreate(fsysproc, nil, STACK); | 
|  | } | 
|  |  | 
|  | void | 
|  | fsysproc(void *v) | 
|  | { | 
|  | int n; | 
|  | Xfid *x; | 
|  | Fid *f; | 
|  | Fcall t; | 
|  | uchar *buf; | 
|  |  | 
|  | USED(v); | 
|  | x = nil; | 
|  | for(;;){ | 
|  | buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */ | 
|  | n = read9pmsg(sfd, buf, messagesize); | 
|  | if(n <= 0){ | 
|  | if(closing) | 
|  | break; | 
|  | error("i/o error on server channel"); | 
|  | } | 
|  | if(x == nil){ | 
|  | sendp(cxfidalloc, nil); | 
|  | x = recvp(cxfidalloc); | 
|  | } | 
|  | x->buf = buf; | 
|  | if(convM2S(buf, n, &x->fcall) != n) | 
|  | error("convert error in convM2S"); | 
|  | if(DEBUG) | 
|  | fprint(2, "%F\n", &x->fcall); | 
|  | if(fcall[x->fcall.type] == nil) | 
|  | x = respond(x, &t, "bad fcall type"); | 
|  | else{ | 
|  | switch(x->fcall.type){ | 
|  | case Tversion: | 
|  | case Tauth: | 
|  | case Tflush: | 
|  | f = nil; | 
|  | break; | 
|  | case Tattach: | 
|  | f = newfid(x->fcall.fid); | 
|  | break; | 
|  | default: | 
|  | f = newfid(x->fcall.fid); | 
|  | if(!f->busy){ | 
|  | x->f = f; | 
|  | x = respond(x, &t, "fid not in use"); | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | } | 
|  | x->f = f; | 
|  | x  = (*fcall[x->fcall.type])(x, f); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Mntdir* | 
|  | fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl) | 
|  | { | 
|  | Mntdir *m; | 
|  | int id; | 
|  |  | 
|  | qlock(&mnt.lk); | 
|  | id = ++mnt.id; | 
|  | m = emalloc(sizeof *m); | 
|  | m->id = id; | 
|  | m->dir =  dir; | 
|  | m->ref = 1;	/* one for Command, one will be incremented in attach */ | 
|  | m->ndir = ndir; | 
|  | m->next = mnt.md; | 
|  | m->incl = incl; | 
|  | m->nincl = nincl; | 
|  | mnt.md = m; | 
|  | qunlock(&mnt.lk); | 
|  | return m; | 
|  | } | 
|  |  | 
|  | void | 
|  | fsysincid(Mntdir *m) | 
|  | { | 
|  | qlock(&mnt.lk); | 
|  | m->ref++; | 
|  | qunlock(&mnt.lk); | 
|  | } | 
|  |  | 
|  | void | 
|  | fsysdelid(Mntdir *idm) | 
|  | { | 
|  | Mntdir *m, *prev; | 
|  | int i; | 
|  | char buf[64]; | 
|  |  | 
|  | if(idm == nil) | 
|  | return; | 
|  | qlock(&mnt.lk); | 
|  | if(--idm->ref > 0){ | 
|  | qunlock(&mnt.lk); | 
|  | return; | 
|  | } | 
|  | prev = nil; | 
|  | for(m=mnt.md; m; m=m->next){ | 
|  | if(m == idm){ | 
|  | if(prev) | 
|  | prev->next = m->next; | 
|  | else | 
|  | mnt.md = m->next; | 
|  | for(i=0; i<m->nincl; i++) | 
|  | free(m->incl[i]); | 
|  | free(m->incl); | 
|  | free(m->dir); | 
|  | free(m); | 
|  | qunlock(&mnt.lk); | 
|  | return; | 
|  | } | 
|  | prev = m; | 
|  | } | 
|  | qunlock(&mnt.lk); | 
|  | sprint(buf, "fsysdelid: can't find id %d\n", idm->id); | 
|  | sendp(cerr, estrdup(buf)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Called only in exec.c:/^run(), from a different FD group | 
|  | */ | 
|  | Mntdir* | 
|  | fsysmount(Rune *dir, int ndir, Rune **incl, int nincl) | 
|  | { | 
|  | return fsysaddid(dir, ndir, incl, nincl); | 
|  | } | 
|  |  | 
|  | void | 
|  | fsysclose(void) | 
|  | { | 
|  | closing = 1; | 
|  | close(sfd); | 
|  | } | 
|  |  | 
|  | Xfid* | 
|  | respond(Xfid *x, Fcall *t, char *err) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | if(err){ | 
|  | t->type = Rerror; | 
|  | t->ename = err; | 
|  | }else | 
|  | t->type = x->fcall.type+1; | 
|  | t->fid = x->fcall.fid; | 
|  | t->tag = x->fcall.tag; | 
|  | if(x->buf == nil) | 
|  | x->buf = emalloc(messagesize); | 
|  | n = convS2M(t, x->buf, messagesize); | 
|  | if(n <= 0) | 
|  | { | 
|  | fprint(2, "convert error (n=%d, msgsize %d): have %F\n", n, messagesize, &x->fcall); | 
|  | fprint(2, "\tresponse: %F\n", t); | 
|  | error("convert error in convS2M"); | 
|  | } | 
|  | if(write(sfd, x->buf, n) != n) | 
|  | error("write error in respond"); | 
|  | free(x->buf); | 
|  | x->buf = nil; | 
|  | if(DEBUG) | 
|  | fprint(2, "r: %F\n", t); | 
|  | return x; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysversion(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | USED(f); | 
|  | if(x->fcall.msize < 256) | 
|  | return respond(x, &t, "version: message size too small"); | 
|  | messagesize = x->fcall.msize; | 
|  | t.msize = messagesize; | 
|  | if(strncmp(x->fcall.version, "9P2000", 6) != 0) | 
|  | return respond(x, &t, "unrecognized 9P version"); | 
|  | t.version = "9P2000"; | 
|  | return respond(x, &t, nil); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysauth(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | USED(f); | 
|  | return respond(x, &t, "acme: authentication not required"); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysflush(Xfid *x, Fid *f) | 
|  | { | 
|  | USED(f); | 
|  | sendp(x->c, (void*)xfidflush); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysattach(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  | int id; | 
|  | Mntdir *m; | 
|  | char buf[128]; | 
|  |  | 
|  | if(strcmp(x->fcall.uname, user) != 0) | 
|  | return respond(x, &t, Eperm); | 
|  | f->busy = TRUE; | 
|  | f->open = FALSE; | 
|  | f->qid.path = Qdir; | 
|  | f->qid.type = QTDIR; | 
|  | f->qid.vers = 0; | 
|  | f->dir = dirtab; | 
|  | f->nrpart = 0; | 
|  | f->w = nil; | 
|  | t.qid = f->qid; | 
|  | f->mntdir = nil; | 
|  | id = atoi(x->fcall.aname); | 
|  | qlock(&mnt.lk); | 
|  | for(m=mnt.md; m; m=m->next) | 
|  | if(m->id == id){ | 
|  | f->mntdir = m; | 
|  | m->ref++; | 
|  | break; | 
|  | } | 
|  | if(m == nil && x->fcall.aname[0]){ | 
|  | snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname); | 
|  | sendp(cerr, estrdup(buf)); | 
|  | } | 
|  | qunlock(&mnt.lk); | 
|  | return respond(x, &t, nil); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsyswalk(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  | int c, i, j, id; | 
|  | Qid q; | 
|  | uchar type; | 
|  | ulong path; | 
|  | Fid *nf; | 
|  | Dirtab *d, *dir; | 
|  | Window *w; | 
|  | char *err; | 
|  |  | 
|  | nf = nil; | 
|  | w = nil; | 
|  | if(f->open) | 
|  | return respond(x, &t, "walk of open file"); | 
|  | if(x->fcall.fid != x->fcall.newfid){ | 
|  | nf = newfid(x->fcall.newfid); | 
|  | if(nf->busy) | 
|  | return respond(x, &t, "newfid already in use"); | 
|  | nf->busy = TRUE; | 
|  | nf->open = FALSE; | 
|  | nf->mntdir = f->mntdir; | 
|  | if(f->mntdir) | 
|  | f->mntdir->ref++; | 
|  | nf->dir = f->dir; | 
|  | nf->qid = f->qid; | 
|  | nf->w = f->w; | 
|  | nf->nrpart = 0;	/* not open, so must be zero */ | 
|  | if(nf->w) | 
|  | incref(&nf->w->ref); | 
|  | f = nf;	/* walk f */ | 
|  | } | 
|  |  | 
|  | t.nwqid = 0; | 
|  | err = nil; | 
|  | dir = nil; | 
|  | id = WIN(f->qid); | 
|  | q = f->qid; | 
|  |  | 
|  | if(x->fcall.nwname > 0){ | 
|  | for(i=0; i<x->fcall.nwname; i++){ | 
|  | if((q.type & QTDIR) == 0){ | 
|  | err = Enotdir; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(strcmp(x->fcall.wname[i], "..") == 0){ | 
|  | type = QTDIR; | 
|  | path = Qdir; | 
|  | id = 0; | 
|  | if(w){ | 
|  | winclose(w); | 
|  | w = nil; | 
|  | } | 
|  | Accept: | 
|  | if(i == MAXWELEM){ | 
|  | err = "name too long"; | 
|  | break; | 
|  | } | 
|  | q.type = type; | 
|  | q.vers = 0; | 
|  | q.path = QID(id, path); | 
|  | t.wqid[t.nwqid++] = q; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* is it a numeric name? */ | 
|  | for(j=0; (c=x->fcall.wname[i][j]); j++) | 
|  | if(c<'0' || '9'<c) | 
|  | goto Regular; | 
|  | /* yes: it's a directory */ | 
|  | if(w)	/* name has form 27/23; get out before losing w */ | 
|  | break; | 
|  | id = atoi(x->fcall.wname[i]); | 
|  | qlock(&row.lk); | 
|  | w = lookid(id, FALSE); | 
|  | if(w == nil){ | 
|  | qunlock(&row.lk); | 
|  | break; | 
|  | } | 
|  | incref(&w->ref);	/* we'll drop reference at end if there's an error */ | 
|  | path = Qdir; | 
|  | type = QTDIR; | 
|  | qunlock(&row.lk); | 
|  | dir = dirtabw; | 
|  | goto Accept; | 
|  |  | 
|  | Regular: | 
|  | //			if(FILE(f->qid) == Qacme)	/* empty directory */ | 
|  | //				break; | 
|  | if(strcmp(x->fcall.wname[i], "new") == 0){ | 
|  | if(w) | 
|  | error("w set in walk to new"); | 
|  | sendp(cnewwindow, nil);	/* signal newwindowthread */ | 
|  | w = recvp(cnewwindow);	/* receive new window */ | 
|  | incref(&w->ref); | 
|  | type = QTDIR; | 
|  | path = QID(w->id, Qdir); | 
|  | id = w->id; | 
|  | dir = dirtabw; | 
|  | goto Accept; | 
|  | } | 
|  |  | 
|  | if(id == 0) | 
|  | d = dirtab; | 
|  | else | 
|  | d = dirtabw; | 
|  | d++;	/* skip '.' */ | 
|  | for(; d->name; d++) | 
|  | if(strcmp(x->fcall.wname[i], d->name) == 0){ | 
|  | path = d->qid; | 
|  | type = d->type; | 
|  | dir = d; | 
|  | goto Accept; | 
|  | } | 
|  |  | 
|  | break;	/* file not found */ | 
|  | } | 
|  |  | 
|  | if(i==0 && err == nil) | 
|  | err = Eexist; | 
|  | } | 
|  |  | 
|  | if(err!=nil || t.nwqid<x->fcall.nwname){ | 
|  | if(nf){ | 
|  | nf->busy = FALSE; | 
|  | fsysdelid(nf->mntdir); | 
|  | } | 
|  | }else if(t.nwqid  == x->fcall.nwname){ | 
|  | if(w){ | 
|  | f->w = w; | 
|  | w = nil;	/* don't drop the reference */ | 
|  | } | 
|  | if(dir) | 
|  | f->dir = dir; | 
|  | f->qid = q; | 
|  | } | 
|  |  | 
|  | if(w != nil) | 
|  | winclose(w); | 
|  |  | 
|  | return respond(x, &t, err); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysopen(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  | int m; | 
|  |  | 
|  | /* can't truncate anything, so just disregard */ | 
|  | x->fcall.mode &= ~(OTRUNC|OCEXEC); | 
|  | /* can't execute or remove anything */ | 
|  | if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE)) | 
|  | goto Deny; | 
|  | switch(x->fcall.mode){ | 
|  | default: | 
|  | goto Deny; | 
|  | case OREAD: | 
|  | m = 0400; | 
|  | break; | 
|  | case OWRITE: | 
|  | m = 0200; | 
|  | break; | 
|  | case ORDWR: | 
|  | m = 0600; | 
|  | break; | 
|  | } | 
|  | if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) | 
|  | goto Deny; | 
|  |  | 
|  | sendp(x->c, (void*)xfidopen); | 
|  | return nil; | 
|  |  | 
|  | Deny: | 
|  | return respond(x, &t, Eperm); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsyscreate(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | USED(f); | 
|  | return respond(x, &t, Eperm); | 
|  | } | 
|  |  | 
|  | static | 
|  | int | 
|  | idcmp(const void *a, const void *b) | 
|  | { | 
|  | return *(int*)a - *(int*)b; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysread(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  | uchar *b; | 
|  | int i, id, n, o, e, j, k, *ids, nids; | 
|  | Dirtab *d, dt; | 
|  | Column *c; | 
|  | uint clock, len; | 
|  | char buf[16]; | 
|  |  | 
|  | if(f->qid.type & QTDIR){ | 
|  | if(FILE(f->qid) == Qacme){	/* empty dir */ | 
|  | t.data = nil; | 
|  | t.count = 0; | 
|  | respond(x, &t, nil); | 
|  | return x; | 
|  | } | 
|  | o = x->fcall.offset; | 
|  | e = x->fcall.offset+x->fcall.count; | 
|  | clock = getclock(); | 
|  | b = emalloc(messagesize); | 
|  | id = WIN(f->qid); | 
|  | n = 0; | 
|  | if(id > 0) | 
|  | d = dirtabw; | 
|  | else | 
|  | d = dirtab; | 
|  | d++;	/* first entry is '.' */ | 
|  | for(i=0; d->name!=nil && i<e; i+=len){ | 
|  | len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock); | 
|  | if(len <= BIT16SZ) | 
|  | break; | 
|  | if(i >= o) | 
|  | n += len; | 
|  | d++; | 
|  | } | 
|  | if(id == 0){ | 
|  | qlock(&row.lk); | 
|  | nids = 0; | 
|  | ids = nil; | 
|  | for(j=0; j<row.ncol; j++){ | 
|  | c = row.col[j]; | 
|  | for(k=0; k<c->nw; k++){ | 
|  | ids = realloc(ids, (nids+1)*sizeof(int)); | 
|  | ids[nids++] = c->w[k]->id; | 
|  | } | 
|  | } | 
|  | qunlock(&row.lk); | 
|  | qsort(ids, nids, sizeof ids[0], idcmp); | 
|  | j = 0; | 
|  | dt.name = buf; | 
|  | for(; j<nids && i<e; i+=len){ | 
|  | k = ids[j]; | 
|  | sprint(dt.name, "%d", k); | 
|  | dt.qid = QID(k, Qdir); | 
|  | dt.type = QTDIR; | 
|  | dt.perm = DMDIR|0700; | 
|  | len = dostat(k, &dt, b+n, x->fcall.count-n, clock); | 
|  | if(len == 0) | 
|  | break; | 
|  | if(i >= o) | 
|  | n += len; | 
|  | j++; | 
|  | } | 
|  | free(ids); | 
|  | } | 
|  | t.data = (char*)b; | 
|  | t.count = n; | 
|  | respond(x, &t, nil); | 
|  | free(b); | 
|  | return x; | 
|  | } | 
|  | sendp(x->c, (void*)xfidread); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsyswrite(Xfid *x, Fid *f) | 
|  | { | 
|  | USED(f); | 
|  | sendp(x->c, (void*)xfidwrite); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysclunk(Xfid *x, Fid *f) | 
|  | { | 
|  | fsysdelid(f->mntdir); | 
|  | sendp(x->c, (void*)xfidclose); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysremove(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | USED(f); | 
|  | return respond(x, &t, Eperm); | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsysstat(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | t.stat = emalloc(messagesize-IOHDRSZ); | 
|  | t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); | 
|  | x = respond(x, &t, nil); | 
|  | free(t.stat); | 
|  | return x; | 
|  | } | 
|  |  | 
|  | static | 
|  | Xfid* | 
|  | fsyswstat(Xfid *x, Fid *f) | 
|  | { | 
|  | Fcall t; | 
|  |  | 
|  | USED(f); | 
|  | return respond(x, &t, Eperm); | 
|  | } | 
|  |  | 
|  | Fid* | 
|  | newfid(int fid) | 
|  | { | 
|  | Fid *f, *ff, **fh; | 
|  |  | 
|  | ff = nil; | 
|  | fh = &fids[fid&(Nhash-1)]; | 
|  | for(f=*fh; f; f=f->next) | 
|  | if(f->fid == fid) | 
|  | return f; | 
|  | else if(ff==nil && f->busy==FALSE) | 
|  | ff = f; | 
|  | if(ff){ | 
|  | ff->fid = fid; | 
|  | return ff; | 
|  | } | 
|  | f = emalloc(sizeof *f); | 
|  | f->fid = fid; | 
|  | f->next = *fh; | 
|  | *fh = f; | 
|  | return f; | 
|  | } | 
|  |  | 
|  | uint | 
|  | getclock(void) | 
|  | { | 
|  | /* | 
|  | char buf[32]; | 
|  |  | 
|  | buf[0] = '\0'; | 
|  | pread(clockfd, buf, sizeof buf, 0); | 
|  | return atoi(buf); | 
|  | */ | 
|  | return time(0); | 
|  | } | 
|  |  | 
|  | int | 
|  | dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) | 
|  | { | 
|  | Dir d; | 
|  |  | 
|  | d.qid.path = QID(id, dir->qid); | 
|  | d.qid.vers = 0; | 
|  | d.qid.type = dir->type; | 
|  | d.mode = dir->perm; | 
|  | d.length = 0;	/* would be nice to do better */ | 
|  | d.name = dir->name; | 
|  | d.uid = user; | 
|  | d.gid = user; | 
|  | d.muid = user; | 
|  | d.atime = clock; | 
|  | d.mtime = clock; | 
|  | return convD2M(&d, buf, nbuf); | 
|  | } |