File system stuff.
diff --git a/src/libfs/COPYRIGHT b/src/libfs/COPYRIGHT
new file mode 100644
index 0000000..de348d1
--- /dev/null
+++ b/src/libfs/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/libfs/close.c b/src/libfs/close.c
new file mode 100644
index 0000000..23388ae
--- /dev/null
+++ b/src/libfs/close.c
@@ -0,0 +1,26 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.h>
+#include "fsimpl.h"
+
+static void
+fidclunk(Fid *fid)
+{
+	Fcall tx, rx;
+
+	tx.type = Tclunk;
+	tx.fid = fid->fid;
+	fsrpc(fid->fs, &tx, &rx, 0);
+	_fsputfid(fid);
+}
+
+void
+fsclose(Fid *fid)
+{
+	/* maybe someday there will be a ref count */
+	fidclunk(fid);
+}
diff --git a/src/libfs/create.c b/src/libfs/create.c
new file mode 100644
index 0000000..ef52a2a
--- /dev/null
+++ b/src/libfs/create.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.h>
+#include "fsimpl.h"
+
+Fid*
+fscreate(Fsys *fs, char *name, int mode, ulong perm)
+{
+	Fid *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/libfs/dirread.c b/src/libfs/dirread.c
new file mode 100644
index 0000000..0ca4064
--- /dev/null
+++ b/src/libfs/dirread.c
@@ -0,0 +1,100 @@
+/* Mostly copied from Plan 9's libc. */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.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(Fid *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(Fid *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/libfs/fs.c b/src/libfs/fs.c
new file mode 100644
index 0000000..985071c
--- /dev/null
+++ b/src/libfs/fs.c
@@ -0,0 +1,276 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.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
+{
+	Fidchunk = 32
+};
+
+Fsys*
+fsinit(int fd)
+{
+	Fsys *fs;
+
+	fs = mallocz(sizeof(Fsys), 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;
+	muxinit(&fs->mux);
+	return fs;
+}
+
+Fid*
+fsroot(Fsys *fs)
+{
+	/* N.B. no incref */
+	return fs->root;
+}
+
+Fsys*
+fsmount(int fd)
+{
+	int n;
+	char *user;
+	Fsys *fs;
+
+	fs = fsinit(fd);
+	if(fs == nil)
+		return nil;
+	strcpy(fs->version, "9P2000");
+	if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){
+	Error:
+		fsunmount(fs);
+		return nil;
+	}
+	fs->msize = n;
+
+	user = getuser();
+	if((fs->root = fsattach(fs, nil, getuser(), "")) == nil)
+		goto Error;
+	return fs;
+}
+
+void
+fsunmount(Fsys *fs)
+{
+	_fsdecref(fs);
+}
+
+void
+_fsdecref(Fsys *fs)
+{
+	Fid *f, *next;
+
+	qlock(&fs->lk);
+	if(--fs->ref == 0){
+		close(fs->fd);
+		for(f=fs->freefid; f; f=next){
+			next = f->next;
+			if(f->fid%Fidchunk == 0)
+				free(f);
+		}
+		free(fs);
+	}
+	qunlock(&fs->lk);
+}
+
+int
+fsversion(Fsys *fs, int msize, char *version, int nversion)
+{
+	void *freep;
+	Fcall tx, rx;
+
+	tx.type = Tversion;
+	tx.version = version;
+	tx.msize = msize;
+
+	if(fsrpc(fs, &tx, &rx, &freep) < 0)
+		return -1;
+	strecpy(version, version+nversion, rx.version);
+	free(freep);
+	return rx.msize;
+}
+
+Fid*
+fsattach(Fsys *fs, Fid *afid, char *user, char *aname)
+{
+	Fcall tx, rx;
+	Fid *fid;
+
+	if((fid = _fsgetfid(fs)) == nil)
+		return nil;
+
+	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;
+}
+
+int
+fsrpc(Fsys *fs, Fcall *tx, Fcall *rx, void **freep)
+{
+	int n, nn;
+	void *tpkt, *rpkt;
+
+	n = sizeS2M(tx);
+	tpkt = malloc(n);
+	if(tpkt == nil)
+		return -1;
+	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");
+		fprint(2, "%r\n");
+		return -1;
+	}
+	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;
+}
+
+Fid*
+_fsgetfid(Fsys *fs)
+{
+	int i;
+	Fid *f;
+
+	qlock(&fs->lk);
+	if(fs->freefid == nil){
+		f = malloc(sizeof(Fid)*Fidchunk);
+		if(f == nil){
+			qunlock(&fs->lk);
+			return nil;
+		}
+		for(i=0; i<Fidchunk; i++){
+			f[i].fid = fs->nextfid++;
+			f[i].next = &f[i+1];
+			f[i].fs = fs;
+			fs->ref++;
+		}
+		f[i-1].next = nil;
+		fs->freefid = f;
+	}
+	f = fs->freefid;
+	fs->freefid = f->next;
+	qunlock(&fs->lk);
+	return f;
+}
+
+void
+_fsputfid(Fid *f)
+{
+	Fsys *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)
+{
+	Fsys *fs;
+
+	fs = mux->aux;
+	return write(fs->fd, pkt, GBIT32((uchar*)pkt));
+}
+
+static void*
+_fsrecv(Mux *mux)
+{
+	uchar *pkt;
+	uchar buf[4];
+	int n;
+	Fsys *fs;
+
+	fs = mux->aux;
+	n = readn(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(buf, n);
+	if(readn(fs->fd, pkt+4, n-4) != n-4){
+		free(pkt);
+		return nil;
+	}
+#if 0
+	if(pkt[4] == Ropenfd){
+		/* do unix socket crap */
+		sysfatal("no socket crap implemented");
+	}
+#endif
+	return pkt;
+}
diff --git a/src/libfs/fsimpl.h b/src/libfs/fsimpl.h
new file mode 100644
index 0000000..fbcc377
--- /dev/null
+++ b/src/libfs/fsimpl.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+typedef struct Queue Queue;
+Queue *_fsqalloc(void);
+int _fsqsend(Queue*, void*);
+void *_fsqrecv(Queue*);
+void _fsqhangup(Queue*);
+void *_fsnbqrecv(Queue*);
+
+#include <mux.h>
+struct Fsys
+{
+	char version[20];
+	int msize;
+	QLock lk;
+	int fd;
+	int ref;
+	Mux mux;
+	Fid *root;
+	Queue *txq;
+	Queue *rxq;
+	Fid *freefid;
+	int nextfid;
+};
+
+struct Fid
+{
+	int fid;
+	int mode;
+	Fid *next;
+	QLock lk;
+	Fsys *fs;
+	Qid qid;
+	vlong offset;
+};
+
+void _fsdecref(Fsys*);
+void _fsputfid(Fid*);
+Fid *_fsgetfid(Fsys*);
+
diff --git a/src/libfs/mkfile b/src/libfs/mkfile
new file mode 100644
index 0000000..acfb0ae
--- /dev/null
+++ b/src/libfs/mkfile
@@ -0,0 +1,22 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+LIB=libfs.a
+
+OFILES=\
+	close.$O\
+	create.$O\
+	dirread.$O\
+	fs.$O\
+	open.$O\
+	read.$O\
+	stat.$O\
+	walk.$O\
+	write.$O\
+	wstat.$O\
+
+HFILES=\
+	$PLAN9/include/fs.h\
+	$PLAN9/include/mux.h\
+
+<$PLAN9/src/mksyslib
diff --git a/src/libfs/open.c b/src/libfs/open.c
new file mode 100644
index 0000000..458c5da
--- /dev/null
+++ b/src/libfs/open.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.h>
+#include "fsimpl.h"
+
+Fid*
+fsopen(Fsys *fs, char *name, int mode)
+{
+	Fid *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/libfs/read.c b/src/libfs/read.c
new file mode 100644
index 0000000..ca6c628
--- /dev/null
+++ b/src/libfs/read.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.h>
+#include "fsimpl.h"
+
+long
+fspread(Fid *fid, void *buf, long n, vlong offset)
+{
+	Fcall tx, rx;
+	void *freep;
+
+	tx.type = Tread;
+	if(offset == -1){
+		qlock(&fid->lk);
+		tx.offset = fid->offset;
+		qunlock(&fid->lk);
+	}else
+		tx.offset = offset;
+	tx.count = n;
+
+	fsrpc(fid->fs, &tx, &rx, &freep);
+	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);
+			tx.offset += n;
+			qunlock(&fid->lk);
+		}
+	}
+	free(freep);
+	
+	return rx.count;
+}
+
+long
+fsread(Fid *fid, void *buf, long n)
+{
+	return fspread(fid, buf, n, -1);
+}
diff --git a/src/libfs/stat.c b/src/libfs/stat.c
new file mode 100644
index 0000000..e55e0b1
--- /dev/null
+++ b/src/libfs/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 <fs.h>
+#include "fsimpl.h"
+
+Dir*
+fsdirstat(Fsys *fs, char *name)
+{
+	Dir *d;
+	Fid *fid;
+
+	if((fid = fswalk(fs->root, name)) == nil)
+		return nil;
+	
+	d = fsdirfstat(fid);
+	fsclose(fid);
+	return d;
+}
+
+Dir*
+fsdirfstat(Fid *fid)
+{
+	Dir *d;
+	Fsys *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/libfs/walk.c b/src/libfs/walk.c
new file mode 100644
index 0000000..ad4eddc
--- /dev/null
+++ b/src/libfs/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 <fs.h>
+#include "fsimpl.h"
+
+Fid*
+fswalk(Fid *fid, char *oname)
+{
+	char *freep, *name;
+	int i, nwalk;
+	char *p;
+	Fid *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)
+				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/libfs/write.c b/src/libfs/write.c
new file mode 100644
index 0000000..96ecfa8
--- /dev/null
+++ b/src/libfs/write.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
+/* See COPYRIGHT */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <fs.h>
+#include "fsimpl.h"
+
+long
+fspwrite(Fid *fd, void *buf, long n, vlong offset)
+{
+	Fcall tx, rx;
+	void *freep;
+
+	tx.type = Tread;
+	if(offset == -1){
+		qlock(&fd->lk);
+		tx.offset = fd->offset;
+		fd->offset += n;
+		qunlock(&fd->lk);
+	}else
+		tx.offset = offset;
+	tx.count = n;
+	tx.data = buf;
+
+	fsrpc(fd->fs, &tx, &rx, &freep);
+	if(rx.type == Rerror){
+		if(offset == -1){
+			qlock(&fd->lk);
+			fd->offset -= n;
+			qunlock(&fd->lk);
+		}
+		werrstr("%s", rx.ename);
+		free(freep);
+		return -1;
+	}
+	free(freep);
+	return rx.count;
+}
+
+long
+fswrite(Fid *fd, void *buf, long n)
+{
+	return fspwrite(fd, buf, n, -1);
+}
diff --git a/src/libfs/wstat.c b/src/libfs/wstat.c
new file mode 100644
index 0000000..90ae0e0
--- /dev/null
+++ b/src/libfs/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 <fs.h>
+#include "fsimpl.h"
+
+int
+fsdirwstat(Fsys *fs, char *name, Dir *d)
+{
+	int n;
+	Fid *fid;
+
+	if((fid = fswalk(fs->root, name)) == nil)
+		return -1;
+	
+	n = fsdirfwstat(fid, d);
+	fsclose(fid);
+	return n;
+}
+
+int
+fsdirfwstat(Fid *fid, Dir *d)
+{
+	char *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;
+}