tlib9pclient is the new libfs - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 46f79934b79ef526ed42bbe5a565e6b5d884d24a
parent 5ba841dffa1f6cda712ebcff27c55c9d0a672c67
Author: rsc 
Date:   Tue,  4 Jan 2005 21:22:40 +0000

lib9pclient is the new libfs

Diffstat:
  A src/lib9pclient/COPYRIGHT           |      27 +++++++++++++++++++++++++++
  A src/lib9pclient/auth.c              |      38 +++++++++++++++++++++++++++++++
  A src/lib9pclient/close.c             |      29 +++++++++++++++++++++++++++++
  A src/lib9pclient/create.c            |      25 +++++++++++++++++++++++++
  A src/lib9pclient/dirread.c           |      99 +++++++++++++++++++++++++++++++
  A src/lib9pclient/fs.c                |     333 +++++++++++++++++++++++++++++++
  A src/lib9pclient/fsimpl.h            |      47 +++++++++++++++++++++++++++++++
  A src/lib9pclient/mkfile              |      23 +++++++++++++++++++++++
  A src/lib9pclient/ns.c                |      40 +++++++++++++++++++++++++++++++
  A src/lib9pclient/open.c              |      24 ++++++++++++++++++++++++
  A src/lib9pclient/openfd.c            |      26 ++++++++++++++++++++++++++
  A src/lib9pclient/read.c              |      72 +++++++++++++++++++++++++++++++
  A src/lib9pclient/stat.c              |      54 +++++++++++++++++++++++++++++++
  A src/lib9pclient/walk.c              |      73 +++++++++++++++++++++++++++++++
  A src/lib9pclient/write.c             |      74 +++++++++++++++++++++++++++++++
  A src/lib9pclient/wstat.c             |      49 +++++++++++++++++++++++++++++++

16 files changed, 1033 insertions(+), 0 deletions(-)
---
diff --git a/src/lib9pclient/COPYRIGHT b/src/lib9pclient/COPYRIGHT
t@@ -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.
+
diff --git a/src/lib9pclient/auth.c b/src/lib9pclient/auth.c
t@@ -0,0 +1,38 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/close.c b/src/lib9pclient/close.c
t@@ -0,0 +1,29 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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);
+}
diff --git a/src/lib9pclient/create.c b/src/lib9pclient/create.c
t@@ -0,0 +1,25 @@
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/dirread.c b/src/lib9pclient/dirread.c
t@@ -0,0 +1,99 @@
+/* Mostly copied from Plan 9's libc. */
+
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/fs.c b/src/lib9pclient/fs.c
t@@ -0,0 +1,333 @@
+/* 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);
+
+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; 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);
+        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;
+}
diff --git a/src/lib9pclient/fsimpl.h b/src/lib9pclient/fsimpl.h
t@@ -0,0 +1,47 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+
+typedef struct Queue Queue;
+Queue *_fsqalloc(void);
+int _fsqsend(Queue*, void*);
+void *_fsqrecv(Queue*);
+void _fsqhangup(Queue*);
+void *_fsnbqrecv(Queue*);
+
+#include 
+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*);
diff --git a/src/lib9pclient/mkfile b/src/lib9pclient/mkfile
t@@ -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
diff --git a/src/lib9pclient/ns.c b/src/lib9pclient/ns.c
t@@ -0,0 +1,40 @@
+#include 
+#include 
+#include 
+#include <9pclient.h>
+#include 
+
+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;
+}
diff --git a/src/lib9pclient/open.c b/src/lib9pclient/open.c
t@@ -0,0 +1,24 @@
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/openfd.c b/src/lib9pclient/openfd.c
t@@ -0,0 +1,26 @@
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/read.c b/src/lib9pclient/read.c
t@@ -0,0 +1,72 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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
diff --git a/src/lib9pclient/stat.c b/src/lib9pclient/stat.c
t@@ -0,0 +1,54 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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;
+}
+
diff --git a/src/lib9pclient/walk.c b/src/lib9pclient/walk.c
t@@ -0,0 +1,73 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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;
+}
diff --git a/src/lib9pclient/write.c b/src/lib9pclient/write.c
t@@ -0,0 +1,74 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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);
+}
diff --git a/src/lib9pclient/wstat.c b/src/lib9pclient/wstat.c
t@@ -0,0 +1,49 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include 
+#include 
+#include 
+#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;
+}