lib9pclient is the new libfs

This commit is contained in:
rsc 2005-01-04 21:22:40 +00:00
parent 5ba841dffa
commit 46f79934b7
16 changed files with 1033 additions and 0 deletions

27
src/lib9pclient/COPYRIGHT Normal file
View file

@ -0,0 +1,27 @@
This software was developed as part of a project at MIT:
/sys/src/libfs/* except dirread.c
/sys/include/fs.h
Copyright (c) 2003 Russ Cox,
Massachusetts Institute of Technology
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

38
src/lib9pclient/auth.c Normal file
View file

@ -0,0 +1,38 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
CFid*
fsauth(CFsys *fsys, char *uname, char *aname)
{
Fcall tx, rx;
void *freep;
CFid *afid;
if((fid = _fsgetfid(fsys)) == nil)
return nil;
tx.type = Tauth;
tx.afid = afid->fid;
tx.uname = uname;
tx.aname = aname;
if(_fsrpc(fsys, &tx, &rx, &freep) < 0){
_fsputfid(afid);
return nil;
}
if(rx.type == Rerror){
werrstr("%s", rx.ename);
free(freep);
_fsputfid(afid);
return nil;
}
afid->qid = rx.aqid;
free(freep);
return afid;
}

29
src/lib9pclient/close.c Normal file
View file

@ -0,0 +1,29 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
static void
fidclunk(CFid *fid)
{
Fcall tx, rx;
tx.type = Tclunk;
tx.fid = fid->fid;
_fsrpc(fid->fs, &tx, &rx, 0);
_fsputfid(fid);
}
void
fsclose(CFid *fid)
{
if(fid == nil)
return;
/* maybe someday there will be a ref count */
fidclunk(fid);
}

25
src/lib9pclient/create.c Normal file
View file

@ -0,0 +1,25 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
CFid*
fscreate(CFsys *fs, char *name, int mode, ulong perm)
{
CFid *fid;
Fcall tx, rx;
if((fid = _fswalk(fs->root, name)) == nil)
return nil;
tx.type = Tcreate;
tx.fid = fid->fid;
tx.mode = mode;
tx.perm = perm;
if(_fsrpc(fs, &tx, &rx, 0) < 0){
fsclose(fid);
return nil;
}
fid->mode = mode;
return fid;
}

99
src/lib9pclient/dirread.c Normal file
View file

@ -0,0 +1,99 @@
/* Mostly copied from Plan 9's libc. */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
static long
dirpackage(uchar *buf, long ts, Dir **d)
{
char *s;
long ss, i, n, nn, m;
*d = nil;
if(ts <= 0)
return 0;
/*
* first find number of all stats, check they look like stats, & size all associated strings
*/
ss = 0;
n = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16(&buf[i]);
if(statcheck(&buf[i], m) < 0)
break;
ss += m;
n++;
}
if(i != ts)
return -1;
*d = malloc(n * sizeof(Dir) + ss);
if(*d == nil)
return -1;
/*
* then convert all buffers
*/
s = (char*)*d + n * sizeof(Dir);
nn = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16((uchar*)&buf[i]);
if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
free(*d);
*d = nil;
return -1;
}
nn++;
s += m;
}
return nn;
}
long
fsdirread(CFid *fid, Dir **d)
{
uchar *buf;
long ts;
buf = malloc(DIRMAX);
if(buf == nil)
return -1;
ts = fsread(fid, buf, DIRMAX);
if(ts >= 0)
ts = dirpackage(buf, ts, d);
free(buf);
return ts;
}
long
fsdirreadall(CFid *fid, Dir **d)
{
uchar *buf, *nbuf;
long n, ts;
buf = nil;
ts = 0;
for(;;){
nbuf = realloc(buf, ts+DIRMAX);
if(nbuf == nil){
free(buf);
return -1;
}
buf = nbuf;
n = fsread(fid, buf+ts, DIRMAX);
if(n <= 0)
break;
ts += n;
}
if(ts >= 0)
ts = dirpackage(buf, ts, d);
free(buf);
if(ts == 0 && n < 0)
return -1;
return ts;
}

333
src/lib9pclient/fs.c Normal file
View file

@ -0,0 +1,333 @@
/* 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);
enum
{
CFidchunk = 32
};
CFsys*
fsinit(int fd)
{
CFsys *fs;
fmtinstall('F', fcallfmt);
fmtinstall('D', dirfmt);
fmtinstall('M', dirmodefmt);
fs = mallocz(sizeof(CFsys), 1);
if(fs == nil)
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);
return fs;
}
CFid*
fsroot(CFsys *fs)
{
/* N.B. no incref */
return fs->root;
}
CFsys*
fsmount(int fd, char *aname)
{
int n;
char *user;
CFsys *fs;
CFid *fid;
fs = fsinit(fd);
if(fs == nil)
return nil;
strcpy(fs->version, "9P2000");
if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){
Error:
fs->fd = -1;
fsunmount(fs);
return nil;
}
fs->msize = n;
user = getuser();
if((fid = fsattach(fs, nil, getuser(), aname)) == nil)
goto Error;
fssetroot(fs, fid);
return 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){
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)
return -1;
strecpy(version, version+nversion, rx.version);
free(freep);
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;
//fprint(2, "<- %F\n", tx);
nn = convS2M(tx, tpkt, n);
if(nn != n){
free(tpkt);
werrstr("libfs: sizeS2M convS2M mismatch");
fprint(2, "%r\n");
return -1;
}
rpkt = muxrpc(&fs->mux, tpkt);
free(tpkt);
if(rpkt == nil)
return -1;
n = GBIT32((uchar*)rpkt);
nn = convM2S(rpkt, n, rx);
if(nn != n){
free(rpkt);
werrstr("libfs: convM2S packet size mismatch %d %d", n, nn);
fprint(2, "%r\n");
return -1;
}
//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);
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;
fs = mux->aux;
return iowrite(fs->iosend, fs->fd, pkt, GBIT32((uchar*)pkt));
}
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)
return nil;
n = GBIT32(buf);
pkt = malloc(n+4);
if(pkt == nil){
fprint(2, "libfs 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;
}

47
src/lib9pclient/fsimpl.h Normal file
View file

@ -0,0 +1,47 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <thread.h>
typedef struct Queue Queue;
Queue *_fsqalloc(void);
int _fsqsend(Queue*, void*);
void *_fsqrecv(Queue*);
void _fsqhangup(Queue*);
void *_fsnbqrecv(Queue*);
#include <mux.h>
struct CFsys
{
char version[20];
int msize;
QLock lk;
int fd;
int ref;
Mux mux;
CFid *root;
Queue *txq;
Queue *rxq;
CFid *freefid;
int nextfid;
Ioproc *iorecv;
Ioproc *iosend;
};
struct CFid
{
int fid;
int mode;
CFid *next;
QLock lk;
CFsys *fs;
Qid qid;
vlong offset;
};
void _fsdecref(CFsys*);
void _fsputfid(CFid*);
CFid *_fsgetfid(CFsys*);
int _fsrpc(CFsys*, Fcall*, Fcall*, void**);
CFid *_fswalk(CFid*, char*);

23
src/lib9pclient/mkfile Normal file
View file

@ -0,0 +1,23 @@
<$PLAN9/src/mkhdr
LIB=lib9pclient.a
OFILES=\
close.$O\
create.$O\
dirread.$O\
fs.$O\
ns.$O\
open.$O\
openfd.$O\
read.$O\
stat.$O\
walk.$O\
write.$O\
wstat.$O\
HFILES=\
$PLAN9/include/9pclient.h\
$PLAN9/include/mux.h\
<$PLAN9/src/mksyslib

40
src/lib9pclient/ns.c Normal file
View file

@ -0,0 +1,40 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include <ctype.h>
CFsys*
nsmount(char *name, char *aname)
{
char *addr, *ns;
int fd;
CFsys *fs;
ns = getns();
if(ns == nil)
return nil;
addr = smprint("unix!%s/%s", ns, name);
free(ns);
if(addr == nil)
return nil;
fd = dial(addr, 0, 0, 0);
if(fd < 0){
werrstr("dial %s: %r", addr);
free(addr);
return nil;
}
free(addr);
fcntl(fd, F_SETFL, FD_CLOEXEC);
fs = fsmount(fd, aname);
if(fs == nil){
close(fd);
return nil;
}
return fs;
}

24
src/lib9pclient/open.c Normal file
View file

@ -0,0 +1,24 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
CFid*
fsopen(CFsys *fs, char *name, int mode)
{
CFid *fid;
Fcall tx, rx;
if((fid = _fswalk(fs->root, name)) == nil)
return nil;
tx.type = Topen;
tx.fid = fid->fid;
tx.mode = mode;
if(_fsrpc(fs, &tx, &rx, 0) < 0){
fsclose(fid);
return nil;
}
fid->mode = mode;
return fid;
}

26
src/lib9pclient/openfd.c Normal file
View file

@ -0,0 +1,26 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
int
fsopenfd(CFsys *fs, char *name, int mode)
{
CFid *fid;
Fcall tx, rx;
if((fid = _fswalk(fs->root, name)) == nil)
return -1;
tx.type = Topenfd;
tx.fid = fid->fid;
tx.mode = mode&~OCEXEC;
if(_fsrpc(fs, &tx, &rx, 0) < 0){
fsclose(fid);
return -1;
}
_fsputfid(fid);
if(mode&OCEXEC && rx.unixfd>=0)
fcntl(rx.unixfd, F_SETFL, FD_CLOEXEC);
return rx.unixfd;
}

72
src/lib9pclient/read.c Normal file
View file

@ -0,0 +1,72 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
long
fspread(CFid *fid, void *buf, long n, vlong offset)
{
Fcall tx, rx;
void *freep;
uint msize;
msize = fid->fs->msize - IOHDRSZ;
if(n > msize)
n = msize;
tx.type = Tread;
tx.fid = fid->fid;
if(offset == -1){
qlock(&fid->lk);
tx.offset = fid->offset;
qunlock(&fid->lk);
}else
tx.offset = offset;
tx.count = n;
if(_fsrpc(fid->fs, &tx, &rx, &freep) < 0)
return -1;
if(rx.type == Rerror){
werrstr("%s", rx.ename);
free(freep);
return -1;
}
if(rx.count){
memmove(buf, rx.data, rx.count);
if(offset == -1){
qlock(&fid->lk);
fid->offset += rx.count;
qunlock(&fid->lk);
}
}
free(freep);
return rx.count;
}
long
fsread(CFid *fid, void *buf, long n)
{
return fspread(fid, buf, n, -1);
}
long
fsreadn(CFid *fid, void *buf, long n)
{
long tot, nn;
for(tot=0; tot<n; tot+=nn){
nn = fsread(fid, (char*)buf+tot, n-tot);
if(nn <= 0){
if(tot == 0)
return nn;
break;
}
}
return tot;
}

54
src/lib9pclient/stat.c Normal file
View file

@ -0,0 +1,54 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
Dir*
fsdirstat(CFsys *fs, char *name)
{
Dir *d;
CFid *fid;
if((fid = _fswalk(fs->root, name)) == nil)
return nil;
d = fsdirfstat(fid);
fsclose(fid);
return d;
}
Dir*
fsdirfstat(CFid *fid)
{
Dir *d;
CFsys *fs;
Fcall tx, rx;
void *freep;
int n;
fs = fid->fs;
tx.type = Tstat;
tx.fid = fid->fid;
if(_fsrpc(fs, &tx, &rx, &freep) < 0)
return nil;
d = malloc(sizeof(Dir)+rx.nstat);
if(d == nil){
free(freep);
return nil;
}
n = convM2D(rx.stat, rx.nstat, d, (char*)&d[1]);
free(freep);
if(n != rx.nstat){
free(d);
werrstr("rx.nstat and convM2D disagree about dir length");
return nil;
}
return d;
}

73
src/lib9pclient/walk.c Normal file
View file

@ -0,0 +1,73 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
CFid*
_fswalk(CFid *fid, char *oname)
{
char *freep, *name;
int i, nwalk;
char *p;
CFid *wfid;
Fcall tx, rx;
freep = nil;
name = oname;
if(name){
freep = malloc(strlen(name)+1);
if(freep == nil)
return nil;
strcpy(freep, name);
name = freep;
}
if((wfid = _fsgetfid(fid->fs)) == nil){
free(freep);
return nil;
}
nwalk = 0;
do{
/* collect names */
for(i=0; name && *name && i < MAXWELEM; ){
p = name;
name = strchr(name, '/');
if(name)
*name++ = 0;
if(*p == 0 || (*p == '.' && *(p+1) == 0))
continue;
tx.wname[i++] = p;
}
/* do a walk */
tx.type = Twalk;
tx.fid = nwalk ? wfid->fid : fid->fid;
tx.newfid = wfid->fid;
tx.nwname = i;
if(_fsrpc(fid->fs, &tx, &rx, 0) < 0){
Error:
free(freep);
if(nwalk)
fsclose(wfid);
else
_fsputfid(wfid);
return nil;
}
if(rx.nwqid != tx.nwname){
/* XXX lame error */
werrstr("file '%s' not found", oname);
goto Error;
}
if(rx.nwqid == 0)
wfid->qid = fid->qid;
else
wfid->qid = rx.wqid[rx.nwqid-1];
nwalk++;
}while(name && *name);
return wfid;
}

74
src/lib9pclient/write.c Normal file
View file

@ -0,0 +1,74 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
static long
_fspwrite(CFid *fid, void *buf, long n, vlong offset)
{
Fcall tx, rx;
void *freep;
tx.type = Twrite;
tx.fid = fid->fid;
if(offset == -1){
qlock(&fid->lk);
tx.offset = fid->offset;
qunlock(&fid->lk);
}else
tx.offset = offset;
tx.count = n;
tx.data = buf;
if(_fsrpc(fid->fs, &tx, &rx, &freep) < 0)
return -1;
if(rx.type == Rerror){
werrstr("%s", rx.ename);
free(freep);
return -1;
}
if(offset == -1 && rx.count){
qlock(&fid->lk);
fid->offset += rx.count;
qunlock(&fid->lk);
}
free(freep);
return rx.count;
}
long
fspwrite(CFid *fid, void *buf, long n, vlong offset)
{
long tot, want, got, first;
uint msize;
msize = fid->fs->msize - IOHDRSZ;
tot = 0;
first = 1;
while(tot < n || first){
want = n - tot;
if(want > msize)
want = msize;
got = _fspwrite(fid, buf, want, offset);
first = 0;
if(got < 0){
if(tot == 0)
return got;
break;
}
tot += got;
if(offset != -1)
offset += got;
}
return tot;
}
long
fswrite(CFid *fid, void *buf, long n)
{
return fspwrite(fid, buf, n, -1);
}

49
src/lib9pclient/wstat.c Normal file
View file

@ -0,0 +1,49 @@
/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
/* See COPYRIGHT */
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <9pclient.h>
#include "fsimpl.h"
int
fsdirwstat(CFsys *fs, char *name, Dir *d)
{
int n;
CFid *fid;
if((fid = _fswalk(fs->root, name)) == nil)
return -1;
n = fsdirfwstat(fid, d);
fsclose(fid);
return n;
}
int
fsdirfwstat(CFid *fid, Dir *d)
{
uchar *a;
int n, nn;
Fcall tx, rx;
n = sizeD2M(d);
a = malloc(n);
if(a == nil)
return -1;
nn = convD2M(d, a, n);
if(n != nn){
werrstr("convD2M and sizeD2M disagree");
free(a);
return -1;
}
tx.type = Twstat;
tx.fid = fid->fid;
tx.stat = a;
tx.nstat = n;
n = _fsrpc(fid->fs, &tx, &rx, 0);
free(a);
return n;
}