lib9pclient is the new libfs
diff --git a/src/lib9pclient/COPYRIGHT b/src/lib9pclient/COPYRIGHT
new file mode 100644
index 0000000..de348d1
--- /dev/null
+++ b/src/lib9pclient/COPYRIGHT
@@ -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
new file mode 100644
index 0000000..2c740e1
--- /dev/null
+++ b/src/lib9pclient/auth.c
@@ -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;
+}
diff --git a/src/lib9pclient/close.c b/src/lib9pclient/close.c
new file mode 100644
index 0000000..98c6cfb
--- /dev/null
+++ b/src/lib9pclient/close.c
@@ -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);
+}
diff --git a/src/lib9pclient/create.c b/src/lib9pclient/create.c
new file mode 100644
index 0000000..cbb4a3a
--- /dev/null
+++ b/src/lib9pclient/create.c
@@ -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;
+}
diff --git a/src/lib9pclient/dirread.c b/src/lib9pclient/dirread.c
new file mode 100644
index 0000000..540cfc6
--- /dev/null
+++ b/src/lib9pclient/dirread.c
@@ -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;
+}
diff --git a/src/lib9pclient/fs.c b/src/lib9pclient/fs.c
new file mode 100644
index 0000000..d72aa4c
--- /dev/null
+++ b/src/lib9pclient/fs.c
@@ -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;
+}
diff --git a/src/lib9pclient/fsimpl.h b/src/lib9pclient/fsimpl.h
new file mode 100644
index 0000000..9c5c555
--- /dev/null
+++ b/src/lib9pclient/fsimpl.h
@@ -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*);
diff --git a/src/lib9pclient/mkfile b/src/lib9pclient/mkfile
new file mode 100644
index 0000000..c6bbc7d
--- /dev/null
+++ b/src/lib9pclient/mkfile
@@ -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
new file mode 100644
index 0000000..9c86547
--- /dev/null
+++ b/src/lib9pclient/ns.c
@@ -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;
+}
diff --git a/src/lib9pclient/open.c b/src/lib9pclient/open.c
new file mode 100644
index 0000000..e9fb981
--- /dev/null
+++ b/src/lib9pclient/open.c
@@ -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;
+}
diff --git a/src/lib9pclient/openfd.c b/src/lib9pclient/openfd.c
new file mode 100644
index 0000000..ef26e5c
--- /dev/null
+++ b/src/lib9pclient/openfd.c
@@ -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;
+}
diff --git a/src/lib9pclient/read.c b/src/lib9pclient/read.c
new file mode 100644
index 0000000..6053728
--- /dev/null
+++ b/src/lib9pclient/read.c
@@ -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;
+}
+			
+
diff --git a/src/lib9pclient/stat.c b/src/lib9pclient/stat.c
new file mode 100644
index 0000000..cb5d826
--- /dev/null
+++ b/src/lib9pclient/stat.c
@@ -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;
+}
+
diff --git a/src/lib9pclient/walk.c b/src/lib9pclient/walk.c
new file mode 100644
index 0000000..32a2fd7
--- /dev/null
+++ b/src/lib9pclient/walk.c
@@ -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;
+}
diff --git a/src/lib9pclient/write.c b/src/lib9pclient/write.c
new file mode 100644
index 0000000..f4eab96
--- /dev/null
+++ b/src/lib9pclient/write.c
@@ -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);
+}
diff --git a/src/lib9pclient/wstat.c b/src/lib9pclient/wstat.c
new file mode 100644
index 0000000..27a2874
--- /dev/null
+++ b/src/lib9pclient/wstat.c
@@ -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;
+}