Initial stab at Venti.
diff --git a/src/cmd/venti/wrarena.c b/src/cmd/venti/wrarena.c
new file mode 100644
index 0000000..f232928
--- /dev/null
+++ b/src/cmd/venti/wrarena.c
@@ -0,0 +1,148 @@
+#include "stdinc.h"
+#include "dat.h"
+#include "fns.h"
+
+char *host;
+int readonly = 1;	/* for part.c */
+
+void
+usage(void)
+{
+	fprint(2, "usage: wrarena [-h host] arenafile [offset]\n");
+	threadexitsall("usage");
+}
+
+static void
+rdarena(VtConn *z, Arena *arena, u64int offset)
+{
+	u64int a, aa, e;
+	u32int magic;
+	Clump cl;
+	uchar score[VtScoreSize];
+	ZBlock *lump;
+
+	fprint(2, "copying %s to venti\n", arena->name);
+	printarena(2, arena);
+
+	a = arena->base;
+	e = arena->base + arena->size;
+	if(offset != ~(u64int)0) {
+		if(offset >= e-a)
+			sysfatal("bad offset %llud >= %llud\n",
+				offset, e-a);
+		aa = offset;
+	} else
+		aa = 0;
+
+	for(; aa < e; aa += ClumpSize+cl.info.size) {
+		magic = clumpmagic(arena, aa);
+		if(magic == ClumpFreeMagic)
+			break;
+		if(magic != ClumpMagic) {
+			fprint(2, "illegal clump magic number %#8.8ux offset %llud\n",
+				magic, aa);
+			break;
+		}
+		lump = loadclump(arena, aa, 0, &cl, score, 0);
+		if(lump == nil) {
+			fprint(2, "clump %llud failed to read: %r\n", aa);
+			break;
+		}
+		if(cl.info.type != VtTypeCorrupt) {
+			scoremem(score, lump->data, cl.info.uncsize);
+			if(scorecmp(cl.info.score, score) != 0) {
+				fprint(2, "clump %llud has mismatched score\n", aa);
+				break;
+			}
+			if(vttypevalid(cl.info.type) < 0) {
+				fprint(2, "clump %llud has bad type %d\n", aa, cl.info.type);
+				break;
+			}
+		}
+		if(z && vtwrite(z, score, cl.info.type, lump->data, cl.info.uncsize) < 0)
+			sysfatal("failed writing clump %llud: %r", aa);
+		freezblock(lump);
+	}
+	if(z && vtsync(z) < 0)
+		sysfatal("failed executing sync: %r");
+
+	print("end offset %llud\n", aa);
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	char *file;
+	VtConn *z;
+	Arena *arena;
+	u64int offset, aoffset;
+	Part *part;
+	Dir *d;
+	uchar buf[8192];
+	ArenaHead head;
+
+	aoffset = 0;
+	ARGBEGIN{
+	case 'h':
+		host = EARGF(usage());
+		break;
+	case 'o':
+		aoffset = strtoull(EARGF(usage()), 0, 0);
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND
+
+	offset = ~(u64int)0;
+	switch(argc) {
+	default:
+		usage();
+	case 2:
+		offset = strtoull(argv[1], 0, 0);
+		/* fall through */
+	case 1:
+		file = argv[0];
+	}
+
+
+	fmtinstall('V', vtscorefmt);
+
+	statsinit();
+
+	if((d = dirstat(file)) == nil)
+		sysfatal("can't stat file %s: %r", file);
+
+	part = initpart(file, 0);
+	if(part == nil)
+		sysfatal("can't open file %s: %r", file);
+	if(readpart(part, aoffset, buf, sizeof buf) < 0)
+		sysfatal("can't read file %s: %r", file);
+
+	if(unpackarenahead(&head, buf) < 0)
+		sysfatal("corrupted arena header: %r");
+
+	if(aoffset+head.size > d->length)
+		sysfatal("arena is truncated: want %llud bytes have %llud\n",
+			head.size, d->length);
+
+	partblocksize(part, head.blocksize);
+	initdcache(8 * MaxDiskBlock);
+
+	arena = initarena(part, aoffset, head.size, head.blocksize);
+	if(arena == nil)
+		sysfatal("initarena: %r");
+
+	if(host && strcmp(host, "/dev/null") != 0){
+		z = vtdial(host);
+		if(z == nil)
+			sysfatal("could not connect to server: %r");
+		if(vtconnect(z) < 0)
+			sysfatal("vtconnect: %r");
+	}else
+		z = nil;
+	
+	rdarena(z, arena, offset);
+	vthangup(z);
+	threadexitsall(0);
+}