375 lines
6.1 KiB
C
375 lines
6.1 KiB
C
/* 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.u");
|
|
if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){
|
|
werrstr("fsversion: %r");
|
|
_fsunmount(fs);
|
|
return nil;
|
|
}
|
|
if(strcmp(fs->version, "9P2000.u") == 0)
|
|
fs->dotu = 1;
|
|
fprint(2, "speaking %d\n", fs->dotu);
|
|
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 = convS2Mu(tx, tpkt, n, fs->dotu);
|
|
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 = convM2Su(rpkt, n, rx, fs->dotu);
|
|
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;
|
|
}
|