shithub: gefs

Download patch

ref: f4834c699a4e3c1782aeefe85e6e48e7473e505a
parent: 3147291a2c825c506f56f3bd77656a7d84cc1677
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Nov 6 10:56:50 EST 2023

gefs: fix snapshot deletion

--- a/cons.c
+++ b/cons.c
@@ -78,7 +78,7 @@
 		na--;
 		ap++;
 	}
-	if(a->delete && na != 1 || na != 2){
+	if(a->delete && na != 1 || !a->delete && na != 2){
 		fprint(fd, "usage: snap -[md] old [new]\n");
 		free(a);
 		return;
--- a/dat.h
+++ b/dat.h
@@ -62,8 +62,8 @@
 	Dlksz	= 1+8+8,		/* tag, death, birth */
 	Dlvsz	= Ptrsz+Ptrsz,		/* hd,tl of deadlist */
 	Dlkvpsz	= Dlksz+Dlvsz,		/* full size of dlist kvp */
-	Linksz	= 1+8+8,		/* gen, prev of snap */
-	Treesz	= 4+4+4+4+8+8+8+Ptrsz,	/* ref, ht, flg, gen, prev, base, root */
+	Treesz	= 4+4+4+4		/* ref, ht, flg, gen, pred, succ, base, root */
+		  +8+8+8+8+Ptrsz,
 	Kvmax	= Keymax + Inlmax,	/* Key and value */
 	Kpmax	= Keymax + Ptrsz,	/* Key and pointer */
 	Wstatmax = 4+8+8+8,		/* mode, size, atime, mtime */
@@ -103,7 +103,6 @@
 	Ktref,	/* tag[8] = snapid[]			scratch snapshot label */
 	Ksnap,	/* sid[8] => ref[8], tree[52]:		snapshot root */
 	Kdlist,	/* snap[8] gen[8] => hd[ptr],tl[ptr]	deadlist  */
-	Kslink,	/* snap[8] next[8] => []		snap successor list */
 };
 
 enum {
@@ -405,13 +404,14 @@
 	int	dirty;
 
 	/* on-disk */
-	int	nsucc;	/* number snapshots after us */
+	int	nref;	/* number snapshots forked/after us */
 	int	nlbl;	/* number of labels referring to us */
 	int	ht;	/* height of the tree */
 	uint	flag;	/* flag set */
 	Bptr	bp;	/* block pointer of root */
 	vlong	gen;	/* generation */
-	vlong	prev;	/* previous snapshot */
+	vlong	pred;	/* previous snapshot */
+	vlong	succ;	/* next snapshot */
 	vlong	base;	/* base snapshot */
 
 	Msg	mq[64];
--- a/dump.c
+++ b/dump.c
@@ -37,9 +37,6 @@
 	case Kup:	/* qid[8] => pqid[8]:		parent dir */
 		n = fmtprint(fmt, "up dir:%llx", UNPACK64(k->k+1));
 		break;
-	case Kslink:
-		n = fmtprint(fmt, "slink gen:%llx, succ:%llx", UNPACK64(k->k+1), UNPACK64(k->k+9));
-		break;
 	case Kdlist:
 		n = fmtprint(fmt, "dlist gen:%llx, bgen:%llx", UNPACK64(k->k+1), UNPACK64(k->k+9));
 		break;
@@ -145,9 +142,6 @@
 	case Kup:	/* qid[8] => pqid[8]:		parent dir */
 		n = fmtprint(fmt, "super dir:%llx, name:\"%.*s\")", UNPACK64(v->v+1), v->nv-11, v->v+11);
 		break;
-	case Kslink:
-		n = fmtprint(fmt, "()");
-		break;
 	case Kdlist:
 		n = fmtprint(fmt, "hd:%B, tl:%B", unpackbp(v->v, v->nv), unpackbp(v->v+Ptrsz, v->nv-Ptrsz));
 		break;
@@ -356,8 +350,9 @@
 	fprint(fd, "\tflag\t0x%x\n", t->flag);
 	fprint(fd, "\tgen:\t%lld\n", t->gen);
 	fprint(fd, "\tbase\t%lld\n", t->base);
-	fprint(fd, "\tprev:\t%lld\n", t->prev);
-	fprint(fd, "\tnsucc:\t%d\n", t->nsucc);
+	fprint(fd, "\tpred:\t%lld\n", t->pred);
+	fprint(fd, "\tpred:\t%lld\n", t->succ);
+	fprint(fd, "\tnref:\t%d\n", t->nref);
 	fprint(fd, "\tnlbl:\t%d\n", t->nlbl);
 	fprint(fd, "\tht:\t%d\n", t->ht);
 	fprint(fd, "\tbp:\t%B\n", t->bp);
@@ -474,7 +469,7 @@
 				fprint(fd, "broken: %B\n", hd);
 				break;
 			}
-			fprint(fd, "logsz: %xm logp=%B\n", b->logsz, b->logp);
+			fprint(fd, "logsz: %x logp=%B\n", b->logsz, b->logp);
 			e = b->data + b->logsz;
 			for(p = b->data; p != e; p += 8)
 				fprint(fd, "\tdead: %llx\n", UNPACK64(p));
--- a/fns.h
+++ b/fns.h
@@ -71,7 +71,6 @@
 char*	delsnap(Tree*, vlong, char*);
 char*	freedl(Dlist*, int);
 Tree*	opensnap(char*, int*);
-vlong	successor(vlong);
 
 void	closesnap(Tree*);
 void	reamfs(char*);
@@ -144,7 +143,7 @@
 void	dlist2kv(Dlist*, Kvp*, char*, int);
 void	lbl2kv(char*, vlong, uint, Kvp*, char*, int);
 void	link2kv(vlong, vlong, Kvp*, char*, int);
-void	retag2kv(vlong, int, int, Kvp*, char*, int);
+void	retag2kv(vlong, vlong, vlong, int, int, Kvp*, char*, int);
 void	tree2kv(Tree*, Kvp*, char*, int);
 
 int	kv2dir(Kvp*, Xdir*);
--- a/fs.c
+++ b/fs.c
@@ -36,7 +36,7 @@
 static void
 snapfs(Amsg *a)
 {
-	Mount *mnt;
+	Mount *mnt, *old;
 	vlong succ;
 	Tree *t, *s;
 	char *e;
@@ -57,13 +57,12 @@
 		return;
 	}
 	if(a->delete){
-		succ = successor(t->gen);
-		if(t != nil) {
-			fprint(a->fd, "snap: snap in use: '%s'\n", a->old);
+		if(mnt != nil) {
+			fprint(a->fd, "snap: snap is mounted: '%s'\n", a->old);
 			unlock(&fs->mountlk);
 			return;
 		}
-		if((e = delsnap(t, succ, a->old)) != nil){
+		if((e = delsnap(t, t->succ, a->old)) != nil){
 			fprint(a->fd, "snap: error deleting '%s': %s\n", a->new, e);
 			unlock(&fs->mountlk);
 			return;
@@ -255,7 +254,7 @@
 
 	if(!mnt->mutable)
 		return Erdonly;
-	if(mnt->root->nlbl != 1 || mnt->root->nsucc != 0)
+	if(mnt->root->nlbl != 1 || mnt->root->nref != 0)
 		if((e = updatesnap(&mnt->root, mnt->root, mnt->name)) != nil)
 			return e;
 	return btupsert(mnt->root, m, nm);
--- a/pack.c
+++ b/pack.c
@@ -398,51 +398,26 @@
 }
 
 void
-retag2kv(vlong gen, int dlbl, int dsucc, Kvp *kv, char *buf, int nbuf)
+retag2kv(vlong gen, vlong pred, vlong succ, int dlbl, int dref, Kvp *kv, char *buf, int nbuf)
 {
 	char *p;
 
+	assert(nbuf >= 8+8+1+1);
 	kv->k = buf;
 	if((p = packsnap(buf, nbuf, gen)) == nil)
 		abort();
 	kv->nk = p - buf;
-	kv->v = p;
-	p[0] = dlbl;
-	p[1] = dsucc;
-	kv->nv = 2;
-}
 
-void
-link2kv(vlong gen, vlong succ, Kvp *kv, char *buf, int nbuf)
-{
-	char *p;
-
-	assert(nbuf >= Linksz);
-
-	p = buf;
-	kv->k = p;
-	*p++ = Kslink;
-	PACK64(p, gen);		p += 8;
-	PACK64(p, succ);	p += 8;
-	kv->nk = (p - kv->k);
 	kv->v = p;
-	kv->nv = 0;
+	PACK64(p, pred);	p += 8;
+	PACK64(p, succ);	p += 8;
+	*p = dlbl;		p += 1;
+	*p = dref;		p += 1;
+	kv->nv = p - kv->v;
+assert(kv->nv == 18);
 }
 
 void
-kv2link(Kvp *kv, vlong *gen, vlong *succ)
-{
-	char *p;
-
-	assert(kv->nk >= Linksz);
-	assert(kv->nv == 0);
-
-	p = kv->k+1;
-	*gen = UNPACK64(p);	p += 8;
-	*succ = UNPACK64(p);	//p += 8;
-}
-
-void
 lbl2kv(char *lbl, vlong gen, uint flg, Kvp *kv, char *buf, int nbuf)
 {
 	char *p;
@@ -512,12 +487,13 @@
 {
 	assert(sz >= Treesz);
 	memset(t, 0, sizeof(Tree));
-	t->nsucc = UNPACK32(p);		p += 4;
+	t->nref = UNPACK32(p);		p += 4;
 	t->nlbl = UNPACK32(p);		p += 4;
 	t->ht = UNPACK32(p);		p += 4;
 	t->flag = UNPACK32(p);		p += 4;
 	t->gen = UNPACK64(p);		p += 8;
-	t->prev = UNPACK64(p);		p += 8;
+	t->pred = UNPACK64(p);		p += 8;
+	t->succ = UNPACK64(p);		p += 8;
 	t->base = UNPACK64(p);		p += 8;
 	t->bp.addr = UNPACK64(p);	p += 8;
 	t->bp.hash = UNPACK64(p);	p += 8;
@@ -530,12 +506,13 @@
 packtree(char *p, int sz, Tree *t)
 {
 	assert(sz >= Treesz);
-	PACK32(p, t->nsucc);	p += 4;
+	PACK32(p, t->nref);	p += 4;
 	PACK32(p, t->nlbl);	p += 4;
 	PACK32(p, t->ht);	p += 4;
 	PACK32(p, t->flag);	p += 4;
 	PACK64(p, t->gen);	p += 8;
-	PACK64(p, t->prev);	p += 8;
+	PACK64(p, t->pred);	p += 8;
+	PACK64(p, t->succ);	p += 8;
 	PACK64(p, t->base);	p += 8;
 	PACK64(p, t->bp.addr);	p += 8;
 	PACK64(p, t->bp.hash);	p += 8;
@@ -584,7 +561,7 @@
 	int i;
 
 	assert(sz == Blksz);
-	memcpy(p, "gefs0007", 8);	p += 8;
+	memcpy(p, "gefs0008", 8);	p += 8;
 	PACK32(p, Blksz);		p += 4;
 	PACK32(p, Bufspc);		p += 4;
 	PACK32(p, fi->snap.ht);		p += 4;
@@ -612,7 +589,7 @@
 	int i;
 
 	assert(sz == Blksz);
-	if(memcmp(p, "gefs0007", 8) != 0){
+	if(memcmp(p, "gefs0008", 8) != 0){
 		werrstr("wrong block header %.8s\n", p);
 		return nil;
 	}
--- a/ream.c
+++ b/ream.c
@@ -117,11 +117,11 @@
 	kv.v = p;
 	memset(&t, 0, sizeof(Tree));
 	t.flag = Tforked;
-	t.nsucc = 1;
+	t.nref = 1;
 	t.nlbl = 2;
 	t.ht = 1;
 	t.gen = fs->nextgen++;
-	t.prev = -1ULL;
+	t.pred = -1ULL;
 	t.bp = r->bp;
 	p = packtree(p, e - p, &t);
 	kv.nv = p - kv.v;
@@ -136,11 +136,12 @@
 	kv.nk = p - kv.k;
 	kv.v = p;
 	memset(&t, 0, sizeof(Tree));
-	t.nsucc = 0;
+	t.nref = 0;
 	t.nlbl = 1;
 	t.ht = 1;
 	t.gen = fs->nextgen++;
-	t.prev = 0;
+	t.pred = 0;
+	t.succ = 2;
 	t.bp = a->bp;
 	p = packtree(p, e - p, &t);
 	kv.nv = p - kv.v;
@@ -155,11 +156,12 @@
 	kv.nk = p - kv.k;
 	kv.v = p;
 	memset(&t, 0, sizeof(Tree));
-	t.nsucc = 0;
+	t.nref = 0;
 	t.nlbl = 1;
 	t.ht = 1;
 	t.gen = fs->nextgen++;
-	t.prev = 0;
+	t.pred = 0;
+	t.succ = -1;
 	t.bp = r->bp;
 	p = packtree(p, e - p, &t);
 	kv.nv = p - kv.v;
--- a/snap.c
+++ b/snap.c
@@ -15,12 +15,10 @@
 
 	if(dl->ins == nil)
 		return nil;
-	if(checkflag(dl->ins, Bdirty))
-		enqueue(dl->ins);
+	if(!checkflag(dl->ins, Bdirty))
+		return nil;
 
-	dl->hd = dl->ins->bp;
-	if(dl->hd.addr == dl->tl.addr)
-		dl->tl = dl->hd;
+	enqueue(dl->ins);
 	m.op = Oinsert;
 	dlist2kv(dl, &m, kvbuf, sizeof(kvbuf));
 	return btupsert(&fs->snap, &m, 1);
@@ -148,11 +146,19 @@
 char*
 freedl(Dlist *dl, int docontents)
 {
+	char *e, buf[Kvmax];
 	Bptr bp, fb;
+	Msg m;
 	Blk *b;
 	char *p;
 
 	bp = dl->hd;
+	if(dl != &fs->snapdl){
+		m.op = Odelete;
+		dlist2kv(dl, &m, buf, sizeof(buf));
+		if((e = btupsert(&fs->snap, &m, 1)) != nil)
+			return e;
+	}
 	while(bp.addr != -1){
 		if((b = getblk(bp, 0)) == nil)
 			return Efs;
@@ -172,90 +178,55 @@
 }
 
 static char*
-mergedl(vlong succ, vlong gen, vlong bgen)
+mergedl(vlong merge, vlong gen, vlong bgen)
 {
-	char *err, buf[2][Kvmax];
+	char *e, buf[2][Kvmax];
 	Dlist *d, *m;
 	Msg msg[2];
 	Blk *b;
-//	vlong x;
 
-	err = nil;
-	d = getdl(gen, bgen);
-	m = getdl(succ, bgen);
-	if(d == nil || m == nil){
-		err = Efs;
-		goto Out;
-	}
-	if(m->ins == nil){
-		m->hd = d->hd;
-		m->tl = d->tl;
-		m->ins = d->ins;
+	if((d = getdl(merge, bgen)) == nil)
+		return Enomem;
+	if((m = getdl(gen, bgen)) == nil)
+		return Enomem;
+	/*
+	 * If the dest dlist didn't exist,
+	 * just move the merge dlist over
+	 * and be done with it, otherwise
+	 * chain onto the existing dlist
+	 * tail.
+	 */
+	if(d->hd.addr == -1){
+		assert(d->ins == nil);
+		d->hd = m->hd;
+		d->tl = m->tl;
+		d->ins = m->ins;
+		if(d->ins != nil)
+			holdblk(d->ins);
 	}else{
-		m->hd = d->hd;
-		if((b = getblk(d->tl, 0)) == nil)
-			goto Out;
-		msg[0].op = Odelete;
-		dlist2kv(d, &msg[0], buf[0], sizeof(buf[0]));
-		msg[1].op = Oinsert;
-		dlist2kv(m, &msg[1], buf[1], sizeof(buf[1]));
-		if((err = btupsert(&fs->snap, msg, 2)) != nil)
-			sysfatal("merge deadlists: %s", err);
-
-		packbp(b->buf+2, Blksz, &m->hd);
+		if(m->ins != nil && checkflag(m->ins, Bdirty))
+			enqueue(m->ins);
+		if(d->tl.addr == d->ins->bp.addr)
+			b = holdblk(d->ins);
+		else
+			b = getblk(d->tl, 0);
+		if(b == nil)
+			return Efs;
+		b->logp = m->hd;
+		setflag(b, Bdirty);
 		enqueue(b);
 		dropblk(b);
-
-// TODO: merge
-//		if(m->ins->logsz + d->ins->logsz < Dlspc){
-//			p = d->ins->data;
-//			q = m->ins->data + m->ins->logsz;
-//			for(i = 0; i < d->ins->logsz; i += 8){
-//				m->ins->logsz += 8;
-//				x = UNPACK64(p);
-//				PACK64(q, x);
-//				p += 8;
-//				q += 8;
-//			}
-//		}
 	}
-Out:
-	if(d != nil)
-		putdl(d);
-	if(m != nil)
-		putdl(m);
-	return err;
+	msg[0].op = Odelete;
+	dlist2kv(m, &msg[0], buf[0], sizeof(buf[0]));
+	msg[1].op = Oinsert;
+	dlist2kv(d, &msg[1], buf[1], sizeof(buf[1]));
+	e = btupsert(&fs->snap, msg, 2);
+	putdl(m);
+	putdl(d);
+	return e;
 }
 
-vlong
-successor(vlong gen)
-{
-	char *e, pfx[9];
-	vlong id, succ;
-	Scan s;
-
-	pfx[0] = Kslink;
-	succ = -1;
-	PACK64(pfx+1, gen);
-	btnewscan(&s, pfx, sizeof(pfx));
-	if((e = btenter(&fs->snap, &s)) != nil)
-		goto Err;
-	if((e = btnext(&s, &s.kv)) != nil)
-		goto Err;
-	if(!s.done)
-		kv2link(&s.kv, &id, &succ);
-	if((e = btnext(&s, &s.kv)) != nil)
-		goto Err;
-	if(!s.done)
-		sysfatal("multiple successors in cleanup");
-	btexit(&s);
-	return succ;
-Err:
-	fprint(2, "error getting snap successor: %s", e);
-	btexit(&s);
-	return -1;
-}
-
 static char*
 reclaimblocks(vlong gen, vlong succ, vlong prev)
 {
@@ -262,7 +233,6 @@
 	char *e, pfx[9];
 	Dlist dl;
 	Scan s;
-	Msg m;
 
 	pfx[0] = Kdlist;
 	PACK64(pfx+1, gen);
@@ -276,18 +246,12 @@
 			break;
 		kv2dlist(&s.kv, &dl);
 
-		/*
-		 * delete the dlist before processing it; it's 
-		 * better to leak blocks than to double free.
-		 */
-		m.op = Odelete;
-		m.Kvp = s.kv;
-		if((e = btupsert(&fs->snap, &m, 1)) != nil)
-			break;
-		if(succ == -1 && dl.bgen > prev)
-			e = freedl(&dl, 1);
-		else
+		if(succ != -1 && dl.bgen <= prev)
 			e = mergedl(succ, dl.gen, dl.bgen);
+		else if(dl.bgen <= prev)
+			e = mergedl(prev, dl.gen, dl.bgen);
+		else
+			e = freedl(&dl, 1);
 		if(e != nil)
 			break;
 	}
@@ -328,7 +292,7 @@
 		nm++;
 	}
  
-	if(t->nlbl == 0 && t->nsucc <= 1){
+	if(t->nlbl == 0 && t->nref <= 1){
 		m[nm].op = Odelete;
 		m[nm].k = buf[nm];
 		if((e = packsnap(buf[nm], sizeof(buf[nm]), t->gen)) == nil)
@@ -346,7 +310,7 @@
 		return e;
 	if((e = btupsert(&fs->snap, m, nm)) != nil)
 		return e;
-	if((e = reclaimblocks(t->gen, succ, t->prev)) != nil)
+	if((e = reclaimblocks(t->gen, succ, t->pred)) != nil)
 		return e;
 	return nil;
 }
@@ -376,28 +340,32 @@
 		n->memref = 1;
 		n->dirty = 0;
 		n->nlbl = 1;
-		n->nsucc = 0;
+		n->nref = 0;
 		n->ht = t->ht;
 		n->bp = t->bp;
-		n->prev = t->prev;
+		n->pred = t->pred;
+		n->succ = -1;
 		n->base = t->gen;
 		n->gen = aincv(&fs->nextgen, 1);
 		n->memgen = aincv(&fs->nextgen, 1);
 
 		m[i].op = Oretag;
-		retag2kv(t->gen, 0, 1, &m[i], buf[i], sizeof(buf[i]));
+		retag2kv(t->gen, t->pred, t->succ, 0, 1, &m[i], buf[i], sizeof(buf[i]));
 		i++;
 		m[i].op = Oinsert;
 		lbl2kv(name, n->gen, 1, &m[i], buf[i], sizeof(buf[i]));
 		i++;
+		m[i].op = Oinsert;
+		tree2kv(n, &m[i], buf[i], sizeof(buf[i]));
+		i++;
 	}else{
 		t->nlbl++;
 
 		m[i].op = Oretag;
-		retag2kv(t->gen, 1, 0, &m[i], buf[i], sizeof(buf[i]));
+		retag2kv(t->gen, t->pred, t->succ, 1, 0, &m[i], buf[i], sizeof(buf[i]));
 		i++;
 		m[i].op = Oinsert;
-		t->prev = t->gen;
+		t->pred = t->gen;
 		t->nlbl++;
 		lbl2kv(name, t->gen, 0, &m[i], buf[i], sizeof(buf[i]));
 		i++;
@@ -425,7 +393,7 @@
 
 	/* update the old kvp */
 	o->nlbl--;
-	o->nsucc++;
+	o->nref++;
 
 	/* create the new one */
 
@@ -435,20 +403,20 @@
 	t->dirty = 0;
 
 	t->nlbl = 1;
-	t->nsucc = 0;
+	t->nref = 0;
 	t->ht = o->ht;
 	t->bp = o->bp;
 	t->base = o->base;
-	if(o->nlbl == 0 && o->nsucc == 1)
-		t->prev = o->prev;
+	if(o->nlbl == 0 && o->nref == 1)
+		t->pred = o->pred;
 	else
-		t->prev = o->gen;
+		t->pred = o->gen;
 	t->gen = o->memgen;
 	t->memgen = aincv(&fs->nextgen, 1);
 
 	i = 0;
 	m[i].op = Oretag;
-	retag2kv(o->gen, -1, 1, &m[i], buf[i], sizeof(buf[i]));
+	retag2kv(o->gen, o->pred, t->gen, -1, 1, &m[i], buf[i], sizeof(buf[i]));
 	i++;
 
 	m[i].op = Oinsert;
@@ -456,10 +424,6 @@
 	i++;
 
 	m[i].op = Oinsert;
-	link2kv(t->prev, t->gen, &m[i], buf[i], sizeof(buf[i]));
-	i++;
-
-	m[i].op = Oinsert;
 	lbl2kv(lbl, t->gen, Lmut, &m[i], buf[i], sizeof(buf[i]));
 	i++;
 
@@ -472,7 +436,7 @@
 	o->dirty = 0;
 
 	/* this was the last ref to the snap */
-	if(o->nlbl == 0 && o->nsucc == 1)
+	if(o->nlbl == 0 && o->nref == 1)
 		delsnap(o, -1, nil);
 	closesnap(o);
 	asetp(r, t);
@@ -595,23 +559,18 @@
 			putdl(dl);
 			return -1;
 		}
-		if(dl->ins != nil){
+		if(dl->ins != nil)
 			enqueue(dl->ins);
-			/* enqueuing updates the hashes */
-			if(dl->ins->bp.addr == dl->tl.addr)
-				dl->tl = dl->ins->bp;
-			b->logp = dl->ins->bp;
-		}else{
+		if(dl->tl.addr == -1)
 			dl->tl = b->bp;
-			b->logp = (Bptr){-1, -1, -1};
-		}
-		cacheins(b);
+		b->logp = dl->hd;
 		dl->hd = b->bp;
 		dl->ins = b;
+		cacheins(b);
 	}
 	p = dl->ins->data + dl->ins->logsz;
-	dl->ins->flag |=  Bdirty;
 	dl->ins->logsz += 8;
+	setflag(dl->ins, Bdirty);
 	PACK64(p, bp.addr);
 	if(t != &fs->snap)
 		putdl(dl);
--- a/tree.c
+++ b/tree.c
@@ -400,7 +400,9 @@
 static int
 apply(Kvp *kv, Msg *m, char *buf, int nbuf)
 {
+	char *p;
 	Tree t;
+	vlong v;
 
 	switch(m->op){
 	case Oclearb:
@@ -416,10 +418,13 @@
 		statupdate(kv, m);
 		return 1;
 	case Oretag:
-		assert(m->nv == 2);
 		unpacktree(&t, kv->v, kv->nv);
-		t.nlbl += m->v[0];
-		t.nsucc += m->v[1];
+		p = m->v;
+		t.pred = UNPACK64(p);	p += 8;
+		t.succ = UNPACK64(p);	p += 8;
+		t.nlbl += *p;		p++;
+		t.nref += *p;		p++;
+		assert(p == m->v + m->nv);
 		packtree(kv->v, kv->nv, &t);
 		return 1;
 	default: