/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */ /* See COPYRIGHT */ #include #include #include #include <9pclient.h> #include #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; inextfid++; 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; }