Fix a handful of small one-time memory leaks in vbackup,
and one per-package memory leak (in writethread).
diff --git a/include/venti.h b/include/venti.h
index 9fea5bc..af5bb78 100644
--- a/include/venti.h
+++ b/include/venti.h
@@ -348,6 +348,8 @@
 int vtversion(VtConn *z);
 void vtdebug(VtConn *z, char*, ...);
 void vthangup(VtConn *z);
+int vtgoodbye(VtConn *z);
+
 /* #pragma varargck argpos vtdebug 2 */
 
 /* server */
@@ -412,6 +414,7 @@
 	u32int	used;
 	u32int	used2;
 	u32int	addr;
+	uintptr	pc;
 };
 
 u32int vtglobaltolocal(uchar[VtScoreSize]);
diff --git a/src/cmd/vbackup/queue.c b/src/cmd/vbackup/queue.c
index 91fa221..d9fcd40 100644
--- a/src/cmd/vbackup/queue.c
+++ b/src/cmd/vbackup/queue.c
@@ -62,3 +62,9 @@
 	rwakeup(&q->r);
 	qunlock(&q->lk);
 }
+
+void
+qfree(Queue *q)
+{
+	vtfree(q);
+}
diff --git a/src/cmd/vbackup/queue.h b/src/cmd/vbackup/queue.h
index 6b2317c..0032006 100644
--- a/src/cmd/vbackup/queue.h
+++ b/src/cmd/vbackup/queue.h
@@ -20,3 +20,4 @@
 void	qclose(Queue*);
 Block	*qread(Queue*, u32int*);
 void	qwrite(Queue*, Block*, u32int);
+void	qfree(Queue*);
diff --git a/src/cmd/vbackup/vbackup.c b/src/cmd/vbackup/vbackup.c
index 9e4c3b0..4c9fd38 100644
--- a/src/cmd/vbackup/vbackup.c
+++ b/src/cmd/vbackup/vbackup.c
@@ -284,6 +284,9 @@
 	 * wait for processes to finish
 	 */
 	wlock(&endlk);
+	
+	qfree(qcmp);
+	qfree(qventi);
 
 	if(statustime)
 		print("# %T procs exited: %d blocks changed, %d read, %d written, %d skipped, %d copied\n",
@@ -297,6 +300,8 @@
 		sysfatal("vtfileflush: %r");
 	if(vtfilegetentry(vfile, &e) < 0)
 		sysfatal("vtfilegetentry: %r");
+	vtfileunlock(vfile);
+	vtfileclose(vfile);
 
 	b = vtcacheallocblock(c, VtDirType);
 	if(b == nil)
@@ -336,6 +341,19 @@
 		sysfatal("vtsync: %r");
 	if(statustime)
 		print("# %T synced\n");
+	
+	fsysclose(fsys);
+	diskclose(disk);
+	vtcachefree(zcache);
+	vtgoodbye(z);
+	// Leak here, because I can't seem to make
+	// the vtrecvproc exit.
+	// vtfreeconn(z);
+	free(tmpnam);
+	z = nil;
+	zcache = nil;
+	fsys = nil;
+	disk = nil;
 	threadexitsall(nil);
 }
 
@@ -416,6 +434,7 @@
 		}
 		if(vtwritepacket(z, wr.score, wr.type, wr.p) < 0)
 			sysfatal("vtwritepacket: %r");
+		packetfree(wr.p);
 	}
 }
 
@@ -472,6 +491,7 @@
 	vtcachesetwrite(zcache, nil);
 	for(i=0; i<nwritethread; i++)
 		send(writechan, nil);
+	chanfree(writechan);
 	if(statustime)
 		print("# %T venti proc exiting - nsend %d nrecv %d\n", nsend, nrecv);
 	runlock(&endlk);
@@ -534,6 +554,7 @@
 	if(threadspawnl(fd, "sh", "sh", "-c", cmd, nil) < 0)
 		sysfatal("exec mount|awk (to find mtpt of %s): %r", dev);
 	/* threadspawnl closed p[1] */
+	free(cmd);
 	n = readn(p[0], buf, sizeof buf-1);
 	close(p[0]);
 	if(n <= 0)
diff --git a/src/libdiskfs/cache.c b/src/libdiskfs/cache.c
index f1af6f1..7b06fa4 100644
--- a/src/libdiskfs/cache.c
+++ b/src/libdiskfs/cache.c
@@ -5,7 +5,7 @@
 /*
  * Disk cache.  Caches by offset, so higher levels have 
  * to deal with alignment issues (if we get asked for the
- * blocks at offsets 0 and 1, we'll do two reads.
+ * blocks at offsets 0 and 1, we'll do two reads).
  */
 
 typedef struct DiskCache DiskCache;
diff --git a/src/libdiskfs/ffs.c b/src/libdiskfs/ffs.c
index 7864c71..9607cfb 100644
--- a/src/libdiskfs/ffs.c
+++ b/src/libdiskfs/ffs.c
@@ -46,6 +46,7 @@
 	fsys->_readfile = ffsreadfile;
 	fsys->_readlink = ffsreadlink;
 	fsys->_readdir = ffsreaddir;
+	fsys->_close = ffsclose;
 	fsys->fileblock = ffsxfileblock;
 
 	if(ffssync(fsys) < 0)
diff --git a/src/libventi/cache.c b/src/libventi/cache.c
index 24cb8c2..809e340 100644
--- a/src/libventi/cache.c
+++ b/src/libventi/cache.c
@@ -167,7 +167,7 @@
 	for(i = 0; i < c->nblock; i++){
 		b = &c->block[i];
 		if(b->ref){
-if(1)fprint(2, "a=%ud %V ref=%d\n", b->addr, b->score, b->ref);
+if(1)fprint(2, "a=%ud %V ref=%d pc=%#lux\n", b->addr, b->score, b->ref, (ulong)b->pc);
 			refed++;
 		}
 	}
@@ -333,6 +333,7 @@
 
 	qlock(&b->lk);
 	b->nlock = 1;
+	b->pc = getcallerpc(&c);
 	return b;
 }
 
@@ -352,7 +353,7 @@
 
 	qlock(&b->lk);
 	b->nlock = 1;
-
+	b->pc = getcallerpc(&b);
 	return b;
 }
 
@@ -374,7 +375,10 @@
 	if(addr != NilBlock){
 		if(vttracelevel)
 			fprint(2, "vtcacheglobal %V %d => local\n", score, type);
-		return vtcachelocal(c, addr, type);
+		b = vtcachelocal(c, addr, type);
+		if(b)
+			b->pc = getcallerpc(&c);
+		return b;
 	}
 
 	h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
@@ -404,6 +408,7 @@
 		}
 		if(vttracelevel)
 			fprint(2, "vtcacheglobal %V %d => found in cache; returning\n", score, type);
+		b->pc = getcallerpc(&c);
 		return b;
 	}
 
@@ -451,6 +456,7 @@
 	b->nlock = 1;
 	if(vttracelevel)
 		fprint(2, "vtcacheglobal %V %d => loaded into cache; returning\n", score, type);
+	b->pc = getcallerpc(&b);
 	return b;
 }
 
@@ -573,6 +579,7 @@
 	}
 	memmove(bb->data, b->data, b->c->blocksize);
 	vtblockput(b);
+	bb->pc = getcallerpc(&b);
 	return bb;
 }
 
diff --git a/src/libventi/client.c b/src/libventi/client.c
index 3396386..40ee851 100644
--- a/src/libventi/client.c
+++ b/src/libventi/client.c
@@ -169,3 +169,12 @@
 	return 0;
 }
 
+int
+vtgoodbye(VtConn *z)
+{
+	VtFcall tx, rx;
+	
+	tx.msgtype = VtTgoodbye;
+	vtfcallrpc(z, &tx, &rx);	/* always fails: no VtRgoodbye */
+	return 0;
+}
diff --git a/src/libventi/conn.c b/src/libventi/conn.c
index 990e7db..5b6d543 100644
--- a/src/libventi/conn.c
+++ b/src/libventi/conn.c
@@ -32,15 +32,19 @@
 {
 	vthangup(z);
 	qlock(&z->lk);
-	for(;;){
+	/*
+	 * Wait for send and recv procs to notice
+	 * the hangup and clear out the queues.
+	 */
+	while(z->readq || z->writeq){
 		if(z->readq)
 			_vtqhangup(z->readq);
-		else if(z->writeq)
+		if(z->writeq)
 			_vtqhangup(z->writeq);
-		else
-			break;
 		rsleep(&z->rpcfork);
 	}
 	packetfree(z->part);
+	vtfree(z->version);
+	vtfree(z->sid);
 	vtfree(z);
 }
diff --git a/src/libventi/fcall.c b/src/libventi/fcall.c
index 9f721b4..65ee2d1 100644
--- a/src/libventi/fcall.c
+++ b/src/libventi/fcall.c
@@ -205,6 +205,7 @@
 
 Err:
 	werrstr("bad packet");
+	vtfcallclear(f);
 	return -1;
 }
 
diff --git a/src/libventi/file.c b/src/libventi/file.c
index 174561e..6604ef6 100644
--- a/src/libventi/file.c
+++ b/src/libventi/file.c
@@ -699,7 +699,7 @@
 
 	i = mkindices(&e, bn, index);
 	if(i < 0)
-		return nil;
+		goto Err;
 	if(i > DEPTH(e.type)){
 		if(mode == VtOREAD){
 			werrstr("bad address 0x%lux", (ulong)bn);
@@ -726,6 +726,7 @@
 		vtblockput(b);
 		b = bb;
 	}
+	b->pc = getcallerpc(&r);
 	return b;
 Err:
 	vtblockput(b);
@@ -833,6 +834,7 @@
 			b = vtcacheglobal(r->c, r->score, VtDirType);
 			if(b == nil)
 				return nil;
+			b->pc = getcallerpc(&r);
 			return b;
 		}
 		assert(r->parent != nil);
@@ -902,6 +904,7 @@
 	 */
 	assert(r->b == nil);
 	r->b = b;
+	b->pc = getcallerpc(&r);
 	return 0;
 }
 
@@ -948,6 +951,8 @@
 	 */
 	r->b = b;
 	rr->b = bb;
+	b->pc = getcallerpc(&r);
+	bb->pc = getcallerpc(&r);
 	return 0;
 }
 
diff --git a/src/libventi/hangup.c b/src/libventi/hangup.c
index 5a992e7..2307586 100644
--- a/src/libventi/hangup.c
+++ b/src/libventi/hangup.c
@@ -1,4 +1,5 @@
 #include <u.h>
+#include <sys/socket.h>
 #include <libc.h>
 #include <venti.h>
 #include "queue.h"
@@ -8,6 +9,9 @@
 {
 	qlock(&z->lk);
 	z->state = VtStateClosed;
+	/* try to make the read in vtsendproc fail */
+	shutdown(SHUT_WR, z->infd);
+	shutdown(SHUT_WR, z->outfd);
 	if(z->infd >= 0)
 		close(z->infd);
 	if(z->outfd >= 0 && z->outfd != z->infd)
@@ -20,3 +24,4 @@
 		_vtqhangup(z->readq);
 	qunlock(&z->lk);
 }
+
diff --git a/src/libventi/packet.c b/src/libventi/packet.c
index aa5b2a0..9947939 100644
--- a/src/libventi/packet.c
+++ b/src/libventi/packet.c
@@ -756,7 +756,6 @@
 		}
 	}
 }
-	
 
 static Frag *
 fragalloc(Packet *p, int n, int pos, Frag *next)
diff --git a/src/libventi/queue.c b/src/libventi/queue.c
index 6e9bfa8..3a1f6ea 100644
--- a/src/libventi/queue.c
+++ b/src/libventi/queue.c
@@ -29,6 +29,20 @@
 	return q;
 }
 
+void
+_vtqfree(Queue *q)
+{
+	Qel *e;
+	
+	/* Leaks the pointers e->p! */
+	while(q->head){
+		e = q->head;
+		q->head = e->next;
+		free(e);
+	}
+	free(q);
+}
+
 int
 _vtqsend(Queue *q, void *p)
 {
diff --git a/src/libventi/queue.h b/src/libventi/queue.h
index 99e0876..2807783 100644
--- a/src/libventi/queue.h
+++ b/src/libventi/queue.h
@@ -4,3 +4,4 @@
 void *_vtqrecv(Queue*);
 void _vtqhangup(Queue*);
 void *_vtnbqrecv(Queue*);
+void _vtqfree(Queue*);
diff --git a/src/libventi/rpc.c b/src/libventi/rpc.c
index faf8062..5a820a9 100644
--- a/src/libventi/rpc.c
+++ b/src/libventi/rpc.c
@@ -59,12 +59,15 @@
 	if(top == buf){
 		werrstr("first two bytes must be in same packet fragment");
 		packetfree(p);
+		vtfree(r);
 		return nil;
 	}
 	top[1] = tag;
 	qunlock(&z->lk);
-	if(vtsend(z, p) < 0)
+	if(vtsend(z, p) < 0){
+		vtfree(r);
 		return nil;
+	}
 
 	qlock(&z->lk);
 	/* wait for the muxer to give us our packet */
@@ -85,6 +88,7 @@
 			if((p = vtrecv(z)) == nil){
 				werrstr("unexpected eof on venti connection");
 				z->muxer = 0;
+				vtfree(r);
 				return nil;
 			}
 			qlock(&z->lk);
diff --git a/src/libventi/send.c b/src/libventi/send.c
index 1d8477d..7ada51a 100644
--- a/src/libventi/send.c
+++ b/src/libventi/send.c
@@ -147,7 +147,7 @@
 	_vtqhangup(q);
 	while((p = _vtnbqrecv(q)) != nil)
 		packetfree(p);
-	vtfree(q);
+	_vtqfree(q);
 	z->readq = nil;
 	rwakeup(&z->rpcfork);
 	qunlock(&z->lk);
@@ -178,7 +178,7 @@
 	_vtqhangup(q);
 	while((p = _vtnbqrecv(q)) != nil)
 		packetfree(p);
-	vtfree(q);
+	_vtqfree(q);
 	z->writeq = nil;
 	rwakeup(&z->rpcfork);
 	qunlock(&z->lk);