tlib9pclient is the new libfs - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 46f79934b79ef526ed42bbe5a565e6b5d884d24a
 (DIR) parent 5ba841dffa1f6cda712ebcff27c55c9d0a672c67
 (HTM) Author: rsc <devnull@localhost>
       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(-)
       ---
 (DIR) 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.
       +
 (DIR) 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 <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;
       +}
 (DIR) 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 <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);
       +}
 (DIR) diff --git a/src/lib9pclient/create.c b/src/lib9pclient/create.c
       t@@ -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;
       +}
 (DIR) 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 <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;
       +}
 (DIR) 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 <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;
       +}
 (DIR) 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 <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*);
 (DIR) 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
 (DIR) diff --git a/src/lib9pclient/ns.c b/src/lib9pclient/ns.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/lib9pclient/open.c b/src/lib9pclient/open.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/lib9pclient/openfd.c b/src/lib9pclient/openfd.c
       t@@ -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;
       +}
 (DIR) 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 <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;
       +}
       +                        
       +
 (DIR) 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 <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;
       +}
       +
 (DIR) 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 <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;
       +}
 (DIR) 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 <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);
       +}
 (DIR) 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 <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;
       +}