diff options
author | rsc <devnull@localhost> | 2003-11-23 17:58:26 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2003-11-23 17:58:26 +0000 |
commit | b8c14089d8f4be73a908f82f62fce80ed2c14a8d (patch) | |
tree | 1d3db32a1ff576873d44d4bef60f13f020d5e10d /src/cmd/plumb/fsys.c | |
parent | 7763a61a3582ef330bca54f225e8ec5325fbd35e (diff) | |
download | plan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.tar.gz plan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.tar.bz2 plan9port-b8c14089d8f4be73a908f82f62fce80ed2c14a8d.zip |
Plan 9 version, nothing tweaked yet.
Diffstat (limited to 'src/cmd/plumb/fsys.c')
-rw-r--r-- | src/cmd/plumb/fsys.c | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/src/cmd/plumb/fsys.c b/src/cmd/plumb/fsys.c new file mode 100644 index 00000000..6f95a23a --- /dev/null +++ b/src/cmd/plumb/fsys.c @@ -0,0 +1,975 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> +#include <thread.h> +#include <auth.h> +#include <fcall.h> +#include <plumb.h> +#include "plumber.h" + +enum +{ + Stack = 8*1024 +}; + +typedef struct Dirtab Dirtab; +typedef struct Fid Fid; +typedef struct Holdq Holdq; +typedef struct Readreq Readreq; +typedef struct Sendreq Sendreq; + +struct Dirtab +{ + char *name; + uchar type; + uint qid; + uint perm; + int nopen; /* #fids open on this port */ + Fid *fopen; + Holdq *holdq; + Readreq *readq; + Sendreq *sendq; +}; + +struct Fid +{ + int fid; + int busy; + int open; + int mode; + Qid qid; + Dirtab *dir; + long offset; /* zeroed at beginning of each message, read or write */ + char *writebuf; /* partial message written so far; offset tells how much */ + Fid *next; + Fid *nextopen; +}; + +struct Readreq +{ + Fid *fid; + Fcall *fcall; + uchar *buf; + Readreq *next; +}; + +struct Sendreq +{ + int nfid; /* number of fids that should receive this message */ + int nleft; /* number left that haven't received it */ + Fid **fid; /* fid[nfid] */ + Plumbmsg *msg; + char *pack; /* plumbpack()ed message */ + int npack; /* length of pack */ + Sendreq *next; +}; + +struct Holdq +{ + Plumbmsg *msg; + Holdq *next; +}; + +struct /* needed because incref() doesn't return value */ +{ + Lock; + int ref; +} rulesref; + +enum +{ + DEBUG = 0, + NDIR = 50, + Nhash = 16, + + Qdir = 0, + Qrules = 1, + Qsend = 2, + Qport = 3, + NQID = Qport +}; + +static Dirtab dir[NDIR] = +{ + { ".", QTDIR, Qdir, 0500|DMDIR }, + { "rules", QTFILE, Qrules, 0600 }, + { "send", QTFILE, Qsend, 0200 }, +}; +static int ndir = NQID; + +static int srvfd; +static int srvclosefd; /* rock for end of pipe to close */ +static int clockfd; +static int clock; +static Fid *fids[Nhash]; +static QLock readlock; +static QLock queue; +static char srvfile[128]; +static int messagesize = 8192+IOHDRSZ; /* good start */ + +static void fsysproc(void*); +static void fsysrespond(Fcall*, uchar*, char*); +static Fid* newfid(int); + +static Fcall* fsysflush(Fcall*, uchar*, Fid*); +static Fcall* fsysversion(Fcall*, uchar*, Fid*); +static Fcall* fsysauth(Fcall*, uchar*, Fid*); +static Fcall* fsysattach(Fcall*, uchar*, Fid*); +static Fcall* fsyswalk(Fcall*, uchar*, Fid*); +static Fcall* fsysopen(Fcall*, uchar*, Fid*); +static Fcall* fsyscreate(Fcall*, uchar*, Fid*); +static Fcall* fsysread(Fcall*, uchar*, Fid*); +static Fcall* fsyswrite(Fcall*, uchar*, Fid*); +static Fcall* fsysclunk(Fcall*, uchar*, Fid*); +static Fcall* fsysremove(Fcall*, uchar*, Fid*); +static Fcall* fsysstat(Fcall*, uchar*, Fid*); +static Fcall* fsyswstat(Fcall*, uchar*, Fid*); + +Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) = +{ + [Tflush] = fsysflush, + [Tversion] = fsysversion, + [Tauth] = fsysauth, + [Tattach] = fsysattach, + [Twalk] = fsyswalk, + [Topen] = fsysopen, + [Tcreate] = fsyscreate, + [Tread] = fsysread, + [Twrite] = fsyswrite, + [Tclunk] = fsysclunk, + [Tremove]= fsysremove, + [Tstat] = fsysstat, + [Twstat] = fsyswstat, +}; + +char Ebadfcall[] = "bad fcall type"; +char Eperm[] = "permission denied"; +char Enomem[] = "malloc failed for buffer"; +char Enotdir[] = "not a directory"; +char Enoexist[] = "plumb file does not exist"; +char Eisdir[] = "file is a directory"; +char Ebadmsg[] = "bad plumb message format"; +char Enosuchport[] ="no such plumb port"; +char Enoport[] = "couldn't find destination for message"; +char Einuse[] = "file already open"; + +/* + * Add new port. A no-op if port already exists or is the null string + */ +void +addport(char *port) +{ + int i; + + if(port == nil) + return; + for(i=NQID; i<ndir; i++) + if(strcmp(port, dir[i].name) == 0) + return; + if(i == NDIR){ + fprint(2, "plumb: too many ports; max %d\n", NDIR); + return; + } + ndir++; + dir[i].name = estrdup(port); + dir[i].qid = i; + dir[i].perm = 0400; + nports++; + ports = erealloc(ports, nports*sizeof(char*)); + ports[nports-1] = dir[i].name; +} + +static ulong +getclock(void) +{ + char buf[32]; + + seek(clockfd, 0, 0); + read(clockfd, buf, sizeof buf); + return atoi(buf); +} + +void +startfsys(void) +{ + int p[2], fd; + + fmtinstall('F', fcallfmt); + clockfd = open("/dev/time", OREAD|OCEXEC); + clock = getclock(); + if(pipe(p) < 0) + error("can't create pipe: %r"); + /* 0 will be server end, 1 will be client end */ + srvfd = p[0]; + srvclosefd = p[1]; + sprint(srvfile, "/srv/plumb.%s.%d", user, getpid()); + if(putenv("plumbsrv", srvfile) < 0) + error("can't write $plumbsrv: %r"); + fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600); + if(fd < 0) + error("can't create /srv file: %r"); + if(fprint(fd, "%d", p[1]) <= 0) + error("can't write /srv/file: %r"); + /* leave fd open; ORCLOSE will take care of it */ + + procrfork(fsysproc, nil, Stack, RFFDG); + + close(p[0]); + if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0) + error("can't mount /mnt/plumb: %r"); + close(p[1]); +} + +static void +fsysproc(void*) +{ + int n; + Fcall *t; + Fid *f; + uchar *buf; + + close(srvclosefd); + srvclosefd = -1; + t = nil; + for(;;){ + buf = malloc(messagesize); /* avoid memset of emalloc */ + if(buf == nil) + error("malloc failed: %r"); + qlock(&readlock); + n = read9pmsg(srvfd, buf, messagesize); + if(n <= 0){ + if(n < 0) + error("i/o error on server channel"); + threadexitsall("unmounted"); + } + if(readlock.head == nil) /* no other processes waiting to read; start one */ + proccreate(fsysproc, nil, Stack); + qunlock(&readlock); + if(t == nil) + t = emalloc(sizeof(Fcall)); + if(convM2S(buf, n, t) != n) + error("convert error in convM2S"); + if(DEBUG) + fprint(2, "<= %F\n", t); + if(fcall[t->type] == nil) + fsysrespond(t, buf, Ebadfcall); + else{ + if(t->type==Tversion || t->type==Tauth) + f = nil; + else + f = newfid(t->fid); + t = (*fcall[t->type])(t, buf, f); + } + } +} + +static void +fsysrespond(Fcall *t, uchar *buf, char *err) +{ + int n; + + if(err){ + t->type = Rerror; + t->ename = err; + }else + t->type++; + if(buf == nil) + buf = emalloc(messagesize); + n = convS2M(t, buf, messagesize); + if(n < 0) + error("convert error in convS2M"); + if(write(srvfd, buf, n) != n) + error("write error in respond"); + if(DEBUG) + fprint(2, "=> %F\n", t); + free(buf); +} + +static +Fid* +newfid(int fid) +{ + Fid *f, *ff, **fh; + + qlock(&queue); + ff = nil; + fh = &fids[fid&(Nhash-1)]; + for(f=*fh; f; f=f->next) + if(f->fid == fid) + goto Return; + else if(ff==nil && !f->busy) + ff = f; + if(ff){ + ff->fid = fid; + f = ff; + goto Return; + } + f = emalloc(sizeof *f); + f->fid = fid; + f->next = *fh; + *fh = f; + Return: + qunlock(&queue); + return f; +} + +static uint +dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock) +{ + Dir d; + + d.qid.type = dir->type; + d.qid.path = dir->qid; + d.qid.vers = 0; + 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); +} + +static void +queuesend(Dirtab *d, Plumbmsg *m) +{ + Sendreq *s, *t; + Fid *f; + int i; + + s = emalloc(sizeof(Sendreq)); + s->nfid = d->nopen; + s->nleft = s->nfid; + s->fid = emalloc(s->nfid*sizeof(Fid*)); + i = 0; + /* build array of fids open on this channel */ + for(f=d->fopen; f!=nil; f=f->nextopen) + s->fid[i++] = f; + s->msg = m; + s->next = nil; + /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */ + for(t=d->sendq; t!=nil; t=t->next) + if(t->next == nil) + break; + if(t == nil) + d->sendq = s; + else + t->next = s; +} + +static void +queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f) +{ + Readreq *r; + + r = emalloc(sizeof(Readreq)); + r->fcall = t; + r->buf = buf; + r->fid = f; + r->next = d->readq; + d->readq = r; +} + +static void +drainqueue(Dirtab *d) +{ + Readreq *r, *nextr, *prevr; + Sendreq *s, *nexts, *prevs; + int i, n; + + prevs = nil; + for(s=d->sendq; s!=nil; s=nexts){ + nexts = s->next; + for(i=0; i<s->nfid; i++){ + prevr = nil; + for(r=d->readq; r!=nil; r=nextr){ + nextr = r->next; + if(r->fid == s->fid[i]){ + /* pack the message if necessary */ + if(s->pack == nil) + s->pack = plumbpack(s->msg, &s->npack); + /* exchange the stuff... */ + r->fcall->data = s->pack+r->fid->offset; + n = s->npack - r->fid->offset; + if(n > messagesize-IOHDRSZ) + n = messagesize-IOHDRSZ; + if(n > r->fcall->count) + n = r->fcall->count; + r->fcall->count = n; + fsysrespond(r->fcall, r->buf, nil); + r->fid->offset += n; + if(r->fid->offset >= s->npack){ + /* message transferred; delete this fid from send queue */ + r->fid->offset = 0; + s->fid[i] = nil; + s->nleft--; + } + /* delete read request from queue */ + if(prevr) + prevr->next = r->next; + else + d->readq = r->next; + free(r->fcall); + free(r); + break; + }else + prevr = r; + } + } + /* if no fids left, delete this send from queue */ + if(s->nleft == 0){ + free(s->fid); + plumbfree(s->msg); + free(s->pack); + if(prevs) + prevs->next = s->next; + else + d->sendq = s->next; + free(s); + }else + prevs = s; + } +} + +/* can't flush a send because they are always answered synchronously */ +static void +flushqueue(Dirtab *d, int oldtag) +{ + Readreq *r, *prevr; + + prevr = nil; + for(r=d->readq; r!=nil; r=r->next){ + if(oldtag == r->fcall->tag){ + /* delete read request from queue */ + if(prevr) + prevr->next = r->next; + else + d->readq = r->next; + free(r->fcall); + free(r->buf); + free(r); + return; + } + prevr = r; + } +} + +/* remove messages awaiting delivery to now-closing fid */ +static void +removesenders(Dirtab *d, Fid *fid) +{ + Sendreq *s, *nexts, *prevs; + int i; + + prevs = nil; + for(s=d->sendq; s!=nil; s=nexts){ + nexts = s->next; + for(i=0; i<s->nfid; i++) + if(fid == s->fid[i]){ + /* delete this fid from send queue */ + s->fid[i] = nil; + s->nleft--; + break; + } + /* if no fids left, delete this send from queue */ + if(s->nleft == 0){ + free(s->fid); + plumbfree(s->msg); + free(s->pack); + if(prevs) + prevs->next = s->next; + else + d->sendq = s->next; + free(s); + }else + prevs = s; + } +} + +static void +hold(Plumbmsg *m, Dirtab *d) +{ + Holdq *h, *q; + + h = emalloc(sizeof(Holdq)); + h->msg = m; + /* add to end of queue */ + if(d->holdq == nil) + d->holdq = h; + else{ + for(q=d->holdq; q->next!=nil; q=q->next) + ; + q->next = h; + } +} + +static void +queueheld(Dirtab *d) +{ + Holdq *h; + + while(d->holdq != nil){ + h = d->holdq; + d->holdq = h->next; + queuesend(d, h->msg); + /* no need to drain queue because we know no-one is reading yet */ + free(h); + } +} + +static void +dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e) +{ + int i; + char *err; + + qlock(&queue); + err = nil; + if(m->dst==nil || m->dst[0]=='\0'){ + err = Enoport; + if(rs != nil) + err = startup(rs, e); + plumbfree(m); + }else + for(i=NQID; i<ndir; i++) + if(strcmp(m->dst, dir[i].name) == 0){ + if(dir[i].nopen == 0){ + err = startup(rs, e); + if(e!=nil && e->holdforclient) + hold(m, &dir[i]); + else + plumbfree(m); + }else{ + queuesend(&dir[i], m); + drainqueue(&dir[i]); + } + break; + } + freeexec(e); + qunlock(&queue); + fsysrespond(t, buf, err); + free(t); +} + +static Fcall* +fsysversion(Fcall *t, uchar *buf, Fid*) +{ + if(t->msize < 256){ + fsysrespond(t, buf, "version: message size too small"); + return t; + } + if(t->msize < messagesize) + messagesize = t->msize; + t->msize = messagesize; + if(strncmp(t->version, "9P2000", 6) != 0){ + fsysrespond(t, buf, "unrecognized 9P version"); + return t; + } + t->version = "9P2000"; + fsysrespond(t, buf, nil); + return t; +} + +static Fcall* +fsysauth(Fcall *t, uchar *buf, Fid*) +{ + fsysrespond(t, buf, "plumber: authentication not required"); + return t; +} + +static Fcall* +fsysattach(Fcall *t, uchar *buf, Fid *f) +{ + Fcall out; + + if(strcmp(t->uname, user) != 0){ + fsysrespond(&out, buf, Eperm); + return t; + } + f->busy = 1; + f->open = 0; + f->qid.type = QTDIR; + f->qid.path = Qdir; + f->qid.vers = 0; + f->dir = dir; + memset(&out, 0, sizeof(Fcall)); + out.type = t->type; + out.tag = t->tag; + out.fid = f->fid; + out.qid = f->qid; + fsysrespond(&out, buf, nil); + return t; +} + +static Fcall* +fsysflush(Fcall *t, uchar *buf, Fid*) +{ + int i; + + qlock(&queue); + for(i=NQID; i<ndir; i++) + flushqueue(&dir[i], t->oldtag); + qunlock(&queue); + fsysrespond(t, buf, nil); + return t; +} + +static Fcall* +fsyswalk(Fcall *t, uchar *buf, Fid *f) +{ + Fcall out; + Fid *nf; + ulong path; + Dirtab *d, *dir; + Qid q; + int i; + uchar type; + char *err; + + if(f->open){ + fsysrespond(t, buf, "clone of an open fid"); + return t; + } + + nf = nil; + if(t->fid != t->newfid){ + nf = newfid(t->newfid); + if(nf->busy){ + fsysrespond(t, buf, "clone to a busy fid"); + return t; + } + nf->busy = 1; + nf->open = 0; + nf->dir = f->dir; + nf->qid = f->qid; + f = nf; /* walk f */ + } + + out.nwqid = 0; + err = nil; + dir = f->dir; + q = f->qid; + + if(t->nwname > 0){ + for(i=0; i<t->nwname; i++){ + if((q.type & QTDIR) == 0){ + err = Enotdir; + break; + } + if(strcmp(t->wname[i], "..") == 0){ + type = QTDIR; + path = Qdir; + Accept: + q.type = type; + q.vers = 0; + q.path = path; + out.wqid[out.nwqid++] = q; + continue; + } + d = dir; + d++; /* skip '.' */ + for(; d->name; d++) + if(strcmp(t->wname[i], d->name) == 0){ + type = d->type; + path = d->qid; + dir = d; + goto Accept; + } + err = Enoexist; + break; + } + } + + out.type = t->type; + out.tag = t->tag; + if(err!=nil || out.nwqid<t->nwname){ + if(nf) + nf->busy = 0; + }else if(out.nwqid == t->nwname){ + f->qid = q; + f->dir = dir; + } + + fsysrespond(&out, buf, err); + return t; +} + +static Fcall* +fsysopen(Fcall *t, uchar *buf, Fid *f) +{ + int m, clearrules, mode; + + clearrules = 0; + if(t->mode & OTRUNC){ + if(f->qid.path != Qrules) + goto Deny; + clearrules = 1; + } + /* can't truncate anything, so just disregard */ + mode = t->mode & ~(OTRUNC|OCEXEC); + /* can't execute or remove anything */ + if(mode==OEXEC || (mode&ORCLOSE)) + goto Deny; + switch(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; + if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){ + lock(&rulesref); + if(rulesref.ref++ != 0){ + rulesref.ref--; + unlock(&rulesref); + fsysrespond(t, buf, Einuse); + return t; + } + unlock(&rulesref); + } + if(clearrules){ + writerules(nil, 0); + rules[0] = nil; + } + t->qid = f->qid; + t->iounit = 0; + qlock(&queue); + f->mode = mode; + f->open = 1; + f->dir->nopen++; + f->nextopen = f->dir->fopen; + f->dir->fopen = f; + queueheld(f->dir); + qunlock(&queue); + fsysrespond(t, buf, nil); + return t; + + Deny: + fsysrespond(t, buf, Eperm); + return t; +} + +static Fcall* +fsyscreate(Fcall *t, uchar *buf, Fid*) +{ + fsysrespond(t, buf, Eperm); + return t; +} + +static Fcall* +fsysreadrules(Fcall *t, uchar *buf) +{ + char *p; + int n; + + p = printrules(); + n = strlen(p); + t->data = p; + if(t->offset >= n) + t->count = 0; + else{ + t->data = p+t->offset; + if(t->offset+t->count > n) + t->count = n-t->offset; + } + fsysrespond(t, buf, nil); + free(p); + return t; +} + +static Fcall* +fsysread(Fcall *t, uchar *buf, Fid *f) +{ + uchar *b; + int i, n, o, e; + uint len; + Dirtab *d; + uint clock; + + if(f->qid.path != Qdir){ + if(f->qid.path == Qrules) + return fsysreadrules(t, buf); + /* read from port */ + if(f->qid.path < NQID){ + fsysrespond(t, buf, "internal error: unknown read port"); + return t; + } + qlock(&queue); + queueread(f->dir, t, buf, f); + drainqueue(f->dir); + qunlock(&queue); + return nil; + } + o = t->offset; + e = t->offset+t->count; + clock = getclock(); + b = malloc(messagesize-IOHDRSZ); + if(b == nil){ + fsysrespond(t, buf, Enomem); + return t; + } + n = 0; + d = dir; + d++; /* first entry is '.' */ + for(i=0; d->name!=nil && i<e; i+=len){ + len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock); + if(len <= BIT16SZ) + break; + if(i >= o) + n += len; + d++; + } + t->data = (char*)b; + t->count = n; + fsysrespond(t, buf, nil); + free(b); + return t; +} + +static Fcall* +fsyswrite(Fcall *t, uchar *buf, Fid *f) +{ + Plumbmsg *m; + int i, n; + long count; + char *data; + Exec *e; + + switch((int)f->qid.path){ + case Qdir: + fsysrespond(t, buf, Eisdir); + return t; + case Qrules: + clock = getclock(); + fsysrespond(t, buf, writerules(t->data, t->count)); + return t; + case Qsend: + if(f->offset == 0){ + data = t->data; + count = t->count; + }else{ + /* partial message already assembled */ + f->writebuf = erealloc(f->writebuf, f->offset + t->count); + memmove(f->writebuf+f->offset, t->data, t->count); + data = f->writebuf; + count = f->offset+t->count; + } + m = plumbunpackpartial(data, count, &n); + if(m == nil){ + if(n == 0){ + f->offset = 0; + free(f->writebuf); + f->writebuf = nil; + fsysrespond(t, buf, Ebadmsg); + return t; + } + /* can read more... */ + if(f->offset == 0){ + f->writebuf = emalloc(t->count); + memmove(f->writebuf, t->data, t->count); + } + /* else buffer has already been grown */ + f->offset += t->count; + fsysrespond(t, buf, nil); + return t; + } + /* release partial buffer */ + f->offset = 0; + free(f->writebuf); + f->writebuf = nil; + for(i=0; rules[i]; i++) + if((e=matchruleset(m, rules[i])) != nil){ + dispose(t, buf, m, rules[i], e); + return nil; + } + if(m->dst != nil){ + dispose(t, buf, m, nil, nil); + return nil; + } + fsysrespond(t, buf, "no matching plumb rule"); + return t; + } + fsysrespond(t, buf, "internal error: write to unknown file"); + return t; +} + +static Fcall* +fsysstat(Fcall *t, uchar *buf, Fid *f) +{ + t->stat = emalloc(messagesize-IOHDRSZ); + t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock); + fsysrespond(t, buf, nil); + free(t->stat); + t->stat = nil; + return t; +} + +static Fcall* +fsyswstat(Fcall *t, uchar *buf, Fid*) +{ + fsysrespond(t, buf, Eperm); + return t; +} + +static Fcall* +fsysremove(Fcall *t, uchar *buf, Fid*) +{ + fsysrespond(t, buf, Eperm); + return t; +} + +static Fcall* +fsysclunk(Fcall *t, uchar *buf, Fid *f) +{ + Fid *prev, *p; + Dirtab *d; + + qlock(&queue); + if(f->open){ + d = f->dir; + d->nopen--; + if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){ + /* + * just to be sure last rule is parsed; error messages will be lost, though, + * unless last write ended with a blank line + */ + writerules(nil, 0); + lock(&rulesref); + rulesref.ref--; + unlock(&rulesref); + } + prev = nil; + for(p=d->fopen; p; p=p->nextopen){ + if(p == f){ + if(prev) + prev->nextopen = f->nextopen; + else + d->fopen = f->nextopen; + removesenders(d, f); + break; + } + prev = p; + } + } + f->busy = 0; + f->open = 0; + f->offset = 0; + if(f->writebuf != nil){ + free(f->writebuf); + f->writebuf = nil; + } + qunlock(&queue); + fsysrespond(t, buf, nil); + return t; +} |