| /* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */ |
| /* See COPYRIGHT */ |
| |
| #include <u.h> |
| #include <libc.h> |
| #include <fcall.h> |
| #include <9pclient.h> |
| #include <thread.h> |
| #include "fsimpl.h" |
| |
| static int _fssend(Mux*, void*); |
| static void *_fsrecv(Mux*); |
| static int _fsgettag(Mux*, void*); |
| static int _fssettag(Mux*, void*, uint); |
| |
| int chatty9pclient; |
| int eofkill9pclient; |
| |
| enum |
| { |
| CFidchunk = 32 |
| }; |
| |
| CFsys* |
| fsinit(int fd) |
| { |
| CFsys *fs; |
| int n; |
| |
| fmtinstall('F', fcallfmt); |
| fmtinstall('D', dirfmt); |
| fmtinstall('M', dirmodefmt); |
| |
| fs = mallocz(sizeof(CFsys), 1); |
| if(fs == nil){ |
| werrstr("mallocz: %r"); |
| return nil; |
| } |
| fs->fd = fd; |
| fs->ref = 1; |
| fs->mux.aux = fs; |
| fs->mux.mintag = 0; |
| fs->mux.maxtag = 256; |
| fs->mux.send = _fssend; |
| fs->mux.recv = _fsrecv; |
| fs->mux.gettag = _fsgettag; |
| fs->mux.settag = _fssettag; |
| fs->iorecv = ioproc(); |
| fs->iosend = ioproc(); |
| muxinit(&fs->mux); |
| |
| strcpy(fs->version, "9P2000"); |
| if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){ |
| werrstr("fsversion: %r"); |
| _fsunmount(fs); |
| return nil; |
| } |
| fs->msize = n; |
| return fs; |
| } |
| |
| CFid* |
| fsroot(CFsys *fs) |
| { |
| /* N.B. no incref */ |
| return fs->root; |
| } |
| |
| CFsys* |
| fsmount(int fd, char *aname) |
| { |
| CFsys *fs; |
| CFid *fid; |
| |
| fs = fsinit(fd); |
| if(fs == nil) |
| return nil; |
| |
| if((fid = fsattach(fs, nil, getuser(), aname)) == nil){ |
| _fsunmount(fs); |
| return nil; |
| } |
| fssetroot(fs, fid); |
| return fs; |
| } |
| |
| void |
| _fsunmount(CFsys *fs) |
| { |
| fs->fd = -1; |
| fsunmount(fs); |
| } |
| |
| void |
| fsunmount(CFsys *fs) |
| { |
| fsclose(fs->root); |
| fs->root = nil; |
| _fsdecref(fs); |
| } |
| |
| void |
| _fsdecref(CFsys *fs) |
| { |
| CFid *f, **l, *next; |
| |
| qlock(&fs->lk); |
| --fs->ref; |
| /*fprint(2, "fsdecref %p to %d\n", fs, fs->ref); */ |
| if(fs->ref == 0){ |
| if(fs->fd >= 0) |
| close(fs->fd); |
| /* trim the list down to just the first in each chunk */ |
| for(l=&fs->freefid; *l; ){ |
| if((*l)->fid%CFidchunk == 0) |
| l = &(*l)->next; |
| else |
| *l = (*l)->next; |
| } |
| /* now free the list */ |
| for(f=fs->freefid; f; f=next){ |
| next = f->next; |
| free(f); |
| } |
| closeioproc(fs->iorecv); |
| closeioproc(fs->iosend); |
| free(fs); |
| return; |
| } |
| qunlock(&fs->lk); |
| } |
| |
| int |
| fsversion(CFsys *fs, int msize, char *version, int nversion) |
| { |
| void *freep; |
| int r, oldmintag, oldmaxtag; |
| Fcall tx, rx; |
| |
| tx.tag = 0; |
| tx.type = Tversion; |
| tx.version = version; |
| tx.msize = msize; |
| |
| /* |
| * bit of a clumsy hack -- force libmux to use NOTAG as tag. |
| * version can only be sent when there are no other messages |
| * outstanding on the wire, so this is more reasonable than it looks. |
| */ |
| oldmintag = fs->mux.mintag; |
| oldmaxtag = fs->mux.maxtag; |
| fs->mux.mintag = NOTAG; |
| fs->mux.maxtag = NOTAG+1; |
| r = _fsrpc(fs, &tx, &rx, &freep); |
| fs->mux.mintag = oldmintag; |
| fs->mux.maxtag = oldmaxtag; |
| if(r < 0){ |
| werrstr("fsrpc: %r"); |
| return -1; |
| } |
| |
| strecpy(version, version+nversion, rx.version); |
| free(freep); |
| fs->msize = rx.msize; |
| return rx.msize; |
| } |
| |
| CFid* |
| fsattach(CFsys *fs, CFid *afid, char *user, char *aname) |
| { |
| Fcall tx, rx; |
| CFid *fid; |
| |
| if(aname == nil) |
| aname = ""; |
| |
| if((fid = _fsgetfid(fs)) == nil) |
| return nil; |
| |
| tx.tag = 0; |
| tx.type = Tattach; |
| tx.afid = afid ? afid->fid : NOFID; |
| tx.fid = fid->fid; |
| tx.uname = user; |
| tx.aname = aname; |
| |
| if(_fsrpc(fs, &tx, &rx, 0) < 0){ |
| _fsputfid(fid); |
| return nil; |
| } |
| fid->qid = rx.qid; |
| return fid; |
| } |
| |
| void |
| fssetroot(CFsys *fs, CFid *fid) |
| { |
| if(fs->root) |
| _fsputfid(fs->root); |
| fs->root = fid; |
| } |
| |
| int |
| _fsrpc(CFsys *fs, Fcall *tx, Fcall *rx, void **freep) |
| { |
| int n, nn; |
| void *tpkt, *rpkt; |
| |
| n = sizeS2M(tx); |
| tpkt = malloc(n); |
| if(freep) |
| *freep = nil; |
| if(tpkt == nil) |
| return -1; |
| tx->tag = 0; |
| if(chatty9pclient) |
| fprint(2, "<- %F\n", tx); |
| nn = convS2M(tx, tpkt, n); |
| if(nn != n){ |
| free(tpkt); |
| werrstr("lib9pclient: sizeS2M convS2M mismatch"); |
| fprint(2, "%r\n"); |
| return -1; |
| } |
| rpkt = muxrpc(&fs->mux, tpkt); |
| free(tpkt); |
| if(rpkt == nil){ |
| werrstr("muxrpc: %r"); |
| return -1; |
| } |
| n = GBIT32((uchar*)rpkt); |
| nn = convM2S(rpkt, n, rx); |
| if(nn != n){ |
| free(rpkt); |
| werrstr("lib9pclient: convM2S packet size mismatch %d %d", n, nn); |
| fprint(2, "%r\n"); |
| return -1; |
| } |
| if(chatty9pclient) |
| fprint(2, "-> %F\n", rx); |
| if(rx->type == Rerror){ |
| werrstr("%s", rx->ename); |
| free(rpkt); |
| return -1; |
| } |
| if(rx->type != tx->type+1){ |
| werrstr("packet type mismatch -- tx %d rx %d", |
| tx->type, rx->type); |
| free(rpkt); |
| return -1; |
| } |
| if(freep) |
| *freep = rpkt; |
| else |
| free(rpkt); |
| return 0; |
| } |
| |
| CFid* |
| _fsgetfid(CFsys *fs) |
| { |
| int i; |
| CFid *f; |
| |
| qlock(&fs->lk); |
| if(fs->freefid == nil){ |
| f = mallocz(sizeof(CFid)*CFidchunk, 1); |
| if(f == nil){ |
| qunlock(&fs->lk); |
| return nil; |
| } |
| for(i=0; i<CFidchunk; i++){ |
| f[i].fid = fs->nextfid++; |
| f[i].next = &f[i+1]; |
| f[i].fs = fs; |
| } |
| f[i-1].next = nil; |
| fs->freefid = f; |
| } |
| f = fs->freefid; |
| fs->freefid = f->next; |
| fs->ref++; |
| qunlock(&fs->lk); |
| f->offset = 0; |
| f->mode = -1; |
| f->qid.path = 0; |
| f->qid.vers = 0; |
| f->qid.type = 0; |
| return f; |
| } |
| |
| void |
| _fsputfid(CFid *f) |
| { |
| CFsys *fs; |
| |
| fs = f->fs; |
| qlock(&fs->lk); |
| f->next = fs->freefid; |
| fs->freefid = f; |
| qunlock(&fs->lk); |
| _fsdecref(fs); |
| } |
| |
| static int |
| _fsgettag(Mux *mux, void *pkt) |
| { |
| return GBIT16((uchar*)pkt+5); |
| } |
| |
| static int |
| _fssettag(Mux *mux, void *pkt, uint tag) |
| { |
| PBIT16((uchar*)pkt+5, tag); |
| return 0; |
| } |
| |
| static int |
| _fssend(Mux *mux, void *pkt) |
| { |
| CFsys *fs; |
| int n; |
| |
| fs = mux->aux; |
| n = iowrite(fs->iosend, fs->fd, pkt, GBIT32((uchar*)pkt)); |
| if(n < 0 && eofkill9pclient) |
| threadexitsall(nil); |
| return n; |
| } |
| |
| static void* |
| _fsrecv(Mux *mux) |
| { |
| uchar *pkt; |
| uchar buf[4]; |
| int n, nfd; |
| CFsys *fs; |
| |
| fs = mux->aux; |
| n = ioreadn(fs->iorecv, fs->fd, buf, 4); |
| if(n != 4){ |
| if(eofkill9pclient) |
| threadexitsall(nil); |
| return nil; |
| } |
| n = GBIT32(buf); |
| pkt = malloc(n+4); |
| if(pkt == nil){ |
| fprint(2, "lib9pclient out of memory reading 9p packet; here comes trouble\n"); |
| return nil; |
| } |
| PBIT32(pkt, n); |
| if(ioreadn(fs->iorecv, fs->fd, pkt+4, n-4) != n-4){ |
| free(pkt); |
| return nil; |
| } |
| if(pkt[4] == Ropenfd){ |
| if((nfd=iorecvfd(fs->iorecv, fs->fd)) < 0){ |
| fprint(2, "recv fd error: %r\n"); |
| free(pkt); |
| return nil; |
| } |
| PBIT32(pkt+n-4, nfd); |
| } |
| return pkt; |
| } |
| |
| Qid |
| fsqid(CFid *fid) |
| { |
| return fid->qid; |
| } |