ref: 912fdf6efa65da2afce022a84f1a59e7c529f528
parent: 1346eca74c4da4daaf2f14ddc40e348b1f6e64a6
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Nov 3 14:28:33 EDT 2023
snap: add flags and base id to avoid double frees on snap deletion
--- a/check.c
+++ b/check.c
@@ -209,7 +209,7 @@
break;
memcpy(name, s.kv.k+1, s.kv.nk-1);
name[s.kv.nk-1] = 0;
- if((t = opensnap(name)) == nil){
+ if((t = opensnap(name, nil)) == nil){
fprint(2, "invalid snap label %s\n", name);
ok = 0;
break;
--- a/cons.c
+++ b/cons.c
@@ -55,34 +55,40 @@
}
static void
-snapfs(int fd, char **ap, int)
+snapfs(int fd, char **ap, int na)
{
Amsg *a;
- a = mallocz(sizeof(Amsg), 1);
- if(a == nil){
+ if((a = mallocz(sizeof(Amsg), 1)) == nil){
fprint(fd, "alloc sync msg: %r\n");
- goto Error;
+ return;
}
- if(strcmp(ap[0], ap[1]) == 0){
- fprint(fd, "not a new snap: %s\n", ap[1]);
- goto Error;
+
+ a->op = AOsnap;
+ a->fd = fd;
+ if(ap[0][0] == '-'){
+ switch(ap[0][1]){
+ case 'm': a->mutable++; break;
+ case 'd': a->delete++; break;
+ default:
+ fprint(fd, "usage: snap -[md] old [new]\n");
+ free(a);
+ return;
+ }
+ na--;
+ ap++;
}
- if(strcmp(ap[0], "-d") == 0){
- strecpy(a->old, a->old+sizeof(a->old), ap[1]);
- a->new[0] = 0;
- }else{
+ if(a->delete && na != 1 || na != 2){
+ fprint(fd, "usage: snap -[md] old [new]\n");
+ free(a);
+ return;
+ }
+ if(na >= 1)
strecpy(a->old, a->old+sizeof(a->old), ap[0]);
+ if(na >= 2)
strecpy(a->new, a->new+sizeof(a->new), ap[1]);
- }
- a->op = AOsnap;
- a->fd = fd;
sendsync(fd, 0);
chsend(fs->admchan, a);
- return;
-Error:
- free(a);
- return;
}
static void
@@ -183,7 +189,7 @@
vlong pqid;
Scan s;
- if((t = opensnap("main")) == nil){
+ if((t = opensnap("main", nil)) == nil){
fprint(fd, "could not open main snap\n");
return;
}
@@ -282,7 +288,7 @@
{.name="halt", .sub=nil, .minarg=0, .maxarg=0, .fn=haltfs},
{.name="help", .sub=nil, .minarg=0, .maxarg=0, .fn=help},
{.name="permissive", .sub=nil, .minarg=1, .maxarg=1, .fn=permflip},
- {.name="snap", .sub=nil, .minarg=2, .maxarg=2, .fn=snapfs},
+ {.name="snap", .sub=nil, .minarg=2, .maxarg=3, .fn=snapfs},
{.name="stats", .sub=nil, .minarg=0, .maxarg=0, .fn=stats},
{.name="sync", .sub=nil, .minarg=0, .maxarg=0, .fn=syncfs},
{.name="users", .sub=nil, .minarg=0, .maxarg=1, .fn=refreshusers},
--- a/dat.h
+++ b/dat.h
@@ -63,7 +63,7 @@
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+8+8+Ptrsz, /* ref, height, gen, prev, root */
+ Treesz = 4+4+4+4+8+8+8+Ptrsz, /* ref, ht, flg, gen, prev, base, root */
Kvmax = Keymax + Inlmax, /* Key and value */
Kpmax = Keymax + Ptrsz, /* Key and pointer */
Wstatmax = 4+8+8+8, /* mode, size, atime, mtime */
@@ -114,6 +114,14 @@
};
enum {
+ Tforked = 1 << 0,
+};
+
+enum {
+ Lmut = 1 << 0,
+};
+
+enum {
Qdump = 1ULL << 63,
};
@@ -264,7 +272,6 @@
Oclearb, /* free block ptr if exists */
Oclobber, /* remove file if it exists */
Owstat, /* update kvp dirent */
- Orefsnap, /* increment snap refcount by delta */
Nmsgtype, /* maximum message type */
};
@@ -366,6 +373,8 @@
struct { /* AOsnap */
char old[128];
char new[128];
+ int mutable;
+ int delete;
};
struct { /* AOsync */
int halt;
@@ -391,6 +400,7 @@
/* in-memory */
Lock lk;
long memref; /* number of in-memory references to this */
+ vlong memgen; /* wip next generation */
int dirty;
/* on-disk */
@@ -397,10 +407,11 @@
int nsucc; /* number snapshots 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 memgen; /* wip next generation */
vlong gen; /* generation */
vlong prev; /* previous snapshot */
+ vlong base; /* base snapshot */
Msg mq[64];
int qsz;
@@ -595,6 +606,7 @@
long ref;
vlong gen;
char *name;
+ int mutable;
Tree *root; /* EBR protected */
};
--- a/dump.c
+++ b/dump.c
@@ -328,7 +328,7 @@
name = ap[0];
if(strcmp(name, "snap") == 0)
t = &fs->snap;
- else if((t = opensnap(name)) == nil){
+ else if((t = opensnap(name, nil)) == nil){
fprint(fd, "open %s: %r\n", name);
return;
}
@@ -353,7 +353,9 @@
void
showtreeroot(int fd, Tree *t)
{
+ 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, "\tnlbl:\t%d\n", t->nlbl);
@@ -368,6 +370,7 @@
Mount *mnt;
vlong id;
Scan s;
+ uint flg;
int sz;
Tree t;
@@ -395,7 +398,8 @@
}
if(s.done)
break;
- fprint(fd, "label: %P\n", &s.kv);
+ flg = UNPACK32(s.kv.v+1+8);
+ fprint(fd, "label: %P 0x%x\n", &s.kv, flg);
}
btexit(&s);
}
--- a/fns.h
+++ b/fns.h
@@ -67,10 +67,10 @@
void clunkmount(Mount*);
char* updatesnap(Tree**, Tree*, char*);
-char* labelsnap(Tree*, char*);
+char* labelsnap(Tree*, char*, int);
char* delsnap(Tree*, vlong, char*);
char* freedl(Dlist*, int);
-Tree* opensnap(char*);
+Tree* opensnap(char*, int*);
vlong successor(vlong);
void closesnap(Tree*);
@@ -142,7 +142,7 @@
int dir2kv(vlong, Xdir*, Kvp*, char*, int);
int dir2statbuf(Xdir*, char*, int);
void dlist2kv(Dlist*, Kvp*, char*, int);
-void lbl2kv(char*, vlong, Kvp*, char*, int);
+void lbl2kv(char*, vlong, uint, Kvp*, char*, int);
void link2kv(vlong, vlong, Kvp*, char*, int);
void tree2kv(Tree*, Kvp*, char*, int);
--- a/fs.c
+++ b/fs.c
@@ -34,7 +34,7 @@
}
static void
-snapfs(int fd, char *old, char *new)
+snapfs(Amsg *a)
{
Mount *mnt;
vlong succ;
@@ -44,37 +44,37 @@
lock(&fs->mountlk);
t = nil;
for(mnt = fs->mounts; mnt != nil; mnt = mnt->next){
- if(strcmp(old, mnt->name) == 0){
+ if(strcmp(a->old, mnt->name) == 0){
t = agetp(&mnt->root);
ainc(&t->memref);
}
}
- if(strlen(new) == 0 && t != nil) {
- fprint(fd, "snap: open snap '%s'\n", old);
+ if(t == nil && (t = opensnap(a->old, nil)) == nil){
+ fprint(a->fd, "snap: open '%s': does not exist\n", a->old);
unlock(&fs->mountlk);
return;
}
- if(t == nil && (t = opensnap(old)) == nil){
- fprint(fd, "snap: open '%s': does not exist\n", old);
- unlock(&fs->mountlk);
- return;
- }
- if(strlen(new) == 0){
+ if(a->delete){
succ = successor(t->gen);
- if((e = delsnap(t, succ, old)) != nil){
- fprint(fd, "snap: error deleting '%s': %s\n", new, e);
+ if(t != nil) {
+ fprint(a->fd, "snap: snap in use: '%s'\n", a->old);
unlock(&fs->mountlk);
return;
}
+ if((e = delsnap(t, succ, a->old)) != nil){
+ fprint(a->fd, "snap: error deleting '%s': %s\n", a->new, e);
+ unlock(&fs->mountlk);
+ return;
+ }
}else{
- if((s = opensnap(new)) != nil){
- fprint(fd, "snap: already exists '%s'\n", new);
+ if((s = opensnap(a->new, nil)) != nil){
+ fprint(a->fd, "snap: already exists '%s'\n", a->new);
closesnap(s);
unlock(&fs->mountlk);
return;
}
- if((e = labelsnap(t, new)) != nil){
- fprint(fd, "snap: error creating '%s': %s\n", new, e);
+ if((e = labelsnap(t, a->new, a->mutable)) != nil){
+ fprint(a->fd, "snap: error creating '%s': %s\n", a->new, e);
unlock(&fs->mountlk);
return;
}
@@ -83,7 +83,12 @@
unlock(&fs->mountlk);
/* we probably want explicit snapshots to get synced */
sync();
- fprint(fd, "created: %s\n", new);
+ if(a->delete)
+ fprint(a->fd, "deleted: %s\n", a->old);
+ else if(a->mutable)
+ fprint(a->fd, "forked: %s from %s\n", a->new, a->old);
+ else
+ fprint(a->fd, "labeled: %s from %s\n", a->new, a->old);
}
static void
@@ -247,6 +252,8 @@
{
char *e;
+ if(!mnt->mutable)
+ return Erdonly;
if(mnt->root->nlbl != 1 || mnt->root->nsucc != 0)
if((e = updatesnap(&mnt->root, mnt->root, mnt->name)) != nil)
return e;
@@ -410,7 +417,7 @@
mnt = nil;
goto Out;
}
- if((t = opensnap(name)) == nil){
+ if((t = opensnap(name, &mnt->mutable)) == nil){
werrstr("%s", Enosnap);
free(mnt->name);
free(mnt);
@@ -2216,7 +2223,7 @@
case AOsnap:
qlock(&fs->mutlk);
epochstart(id);
- snapfs(a->fd, a->old, a->new);
+ snapfs(a);
epochend(id);
qunlock(&fs->mutlk);
break;
--- a/load.c
+++ b/load.c
@@ -115,7 +115,7 @@
fprint(2, "\tsyncgen:\t%lld\n", fs->qgen);
fprint(2, "\tblocksize:\t%lld\n", Blksz);
fprint(2, "\tcachesz:\t%lld MiB\n", fs->cmax*Blksz/MiB);
- if((t = opensnap("adm")) == nil)
+ if((t = opensnap("adm", nil)) == nil)
sysfatal("load users: no main label");
if((e = loadusers(2, t)) != nil)
sysfatal("load users: %s\n", e);
--- a/pack.c
+++ b/pack.c
@@ -428,20 +428,24 @@
}
void
-lbl2kv(char *lbl, vlong gen, Kvp *kv, char *buf, int nbuf)
+lbl2kv(char *lbl, vlong gen, uint flg, Kvp *kv, char *buf, int nbuf)
{
char *p;
+ int n;
- assert(nbuf >= strlen(lbl) + 9);
+ n = strlen(lbl);
+ assert(nbuf >= 1+n + 1+8+4);
p = buf;
kv->k = p;
- p = packlabel(buf, nbuf, lbl);
+ p[0] = Klabel; p += 1;
+ memcpy(p, lbl, n); p += n;
kv->nk = p - kv->k;
kv->v = p;
- if((p = packsnap(p, nbuf-kv->nk, gen)) == nil)
- abort();
+ p[0] = Ksnap; p += 1;
+ PACK64(p, gen); p += 8;
+ PACK32(p, flg); p += 4;
kv->nv = p - kv->v;
}
@@ -496,8 +500,10 @@
t->nsucc = 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->base = UNPACK64(p); p += 8;
t->bp.addr = UNPACK64(p); p += 8;
t->bp.hash = UNPACK64(p); p += 8;
t->bp.gen = UNPACK64(p); //p += 8;
@@ -512,8 +518,10 @@
PACK32(p, t->nsucc); 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->base); p += 8;
PACK64(p, t->bp.addr); p += 8;
PACK64(p, t->bp.hash); p += 8;
PACK64(p, t->bp.gen); p += 8;
--- a/ream.c
+++ b/ream.c
@@ -96,38 +96,25 @@
static void
initsnap(Blk *s, Blk *r, Blk *a)
{
- char *p, kbuf[Keymax], vbuf[Treesz];
+ char *p, *e, buf[Kvmax];
Tree t;
Kvp kv;
- p = packlabel(kbuf, sizeof(kbuf), "adm");
- kv.k = kbuf;
- kv.nk = p - kbuf;
- p = packsnap(vbuf, sizeof(vbuf), 1);
- kv.v = vbuf;
- kv.nv = p - vbuf;
+ lbl2kv("adm", 1, Lmut, &kv, buf, sizeof(buf));
setval(s, &kv);
-
- p = packlabel(kbuf, sizeof(kbuf), "empty");
- kv.k = kbuf;
- kv.nk = p - kbuf;
- p = packsnap(vbuf, sizeof(vbuf), 0);
- kv.v = vbuf;
- kv.nv = p - vbuf;
+ lbl2kv("empty", 0, 0, &kv, buf, sizeof(buf));
setval(s, &kv);
-
- p = packlabel(kbuf, sizeof(kbuf), "main");
- kv.k = kbuf;
- kv.nk = p - kbuf;
- p = packsnap(vbuf, sizeof(vbuf), 0);
- kv.v = vbuf;
- kv.nv = p - vbuf;
+ lbl2kv("main", 0, Lmut, &kv, buf, sizeof(buf));
setval(s, &kv);
- p = packsnap(kbuf, sizeof(kbuf), 0);
- kv.k = kbuf;
- kv.nk = p - kbuf;
+ p = buf;
+ e = p + sizeof(buf);
+ kv.k = p;
+ p = packsnap(buf, e - p, 0);
+ kv.nk = p - kv.k;
+
+ kv.v = p;
memset(&t, 0, sizeof(Tree));
t.nsucc = 1;
t.nlbl = 2;
@@ -135,15 +122,18 @@
t.gen = fs->nextgen++;
t.prev = -1ULL;
t.bp = r->bp;
- p = packtree(vbuf, sizeof(vbuf), &t);
- kv.v = vbuf;
- kv.nv = p - vbuf;
+ p = packtree(p, e - p, &t);
+ kv.nv = p - kv.v;
setval(s, &kv);
- p = packsnap(kbuf, sizeof(kbuf), 1);
- kv.k = kbuf;
- kv.nk = p - kbuf;
+ p = buf;
+ e = p + sizeof(buf);
+ kv.k = p;
+ p = packsnap(p, e - p, 1);
+ kv.nk = p - kv.k;
+
+ kv.v = p;
memset(&t, 0, sizeof(Tree));
t.nsucc = 0;
t.nlbl = 1;
@@ -151,9 +141,8 @@
t.gen = fs->nextgen++;
t.prev = -1ULL;
t.bp = a->bp;
- p = packtree(vbuf, sizeof(vbuf), &t);
- kv.v = vbuf;
- kv.nv = p - vbuf;
+ p = packtree(p, e - p, &t);
+ kv.nv = p - kv.v;
setval(s, &kv);
}
--- a/snap.c
+++ b/snap.c
@@ -359,22 +359,58 @@
* will show up in the dump.
*/
char*
-labelsnap(Tree *t, char *name)
+labelsnap(Tree *t, char *name, int mutable)
{
- char buf[2][Kvmax];
- Msg m[2];
+ char buf[3][Kvmax];
+ Msg m[3];
+ Tree *n;
+ int i;
if(strcmp(name, "dump") == 0
|| strcmp(name, "empty") == 0
|| strcmp(name, "adm") == 0)
return Ename;
- t->nlbl++;
- m[0].op = Oinsert;
- tree2kv(t, &m[0], buf[0], sizeof(buf[0]));
- m[1].op = Oinsert;
- lbl2kv(name, t->gen, &m[1], buf[1], sizeof(buf[1]));
+
+ i = 0;
+ if(mutable){
+ if((n = mallocz(sizeof(Tree), 1)) == nil)
+ return Enomem;
+ n->memref = 1;
+ n->dirty = 0;
+ n->nlbl = 1;
+ n->nsucc = 0;
+ n->ht = t->ht;
+ n->bp = t->bp;
+ n->prev = t->gen;
+ n->base = t->gen;
+ n->gen = aincv(&fs->nextgen, 1);
+ n->memgen = aincv(&fs->nextgen, 1);
- return btupsert(&fs->snap, m, 2);
+ t->flag |= Tforked;
+ t->nsucc++;
+
+ m[i].op = Oinsert;
+ tree2kv(t, &m[i], buf[i], sizeof(buf[i]));
+ i++;
+
+ m[i].op = Oinsert;
+ tree2kv(n, &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++;
+ }else{
+ t->nlbl++;
+ m[i].op = Oinsert;
+ tree2kv(t, &m[i], buf[i], sizeof(buf[i]));
+ i++;
+
+ m[i].op = Oinsert;
+ lbl2kv(name, t->gen, 0, &m[i], buf[i], sizeof(buf[i]));
+ i++;
+ }
+ return btupsert(&fs->snap, m, i);
}
/*
@@ -401,6 +437,7 @@
tree2kv(o, &m[0], buf[0], sizeof(buf[0]));
/* create the new one */
+
if((t = mallocz(sizeof(Tree), 1)) == nil)
return Enomem;
t->memref = 1;
@@ -410,6 +447,7 @@
t->nsucc = 0;
t->ht = o->ht;
t->bp = o->bp;
+ t->base = o->base;
t->prev = o->gen;
t->gen = o->memgen;
t->memgen = aincv(&fs->nextgen, 1);
@@ -419,7 +457,7 @@
m[2].op = Oinsert;
link2kv(t->prev, t->gen, &m[2], buf[2], sizeof(buf[2]));
m[3].op = Oinsert;
- lbl2kv(lbl, t->gen, &m[3], buf[3], sizeof(buf[3]));
+ lbl2kv(lbl, t->gen, Lmut, &m[3], buf[3], sizeof(buf[3]));
if((e = btupsert(&fs->snap, m, 4)) != nil){
free(t);
return e;
@@ -442,10 +480,11 @@
* open snapshot by label, returning a tree.
*/
Tree*
-opensnap(char *label)
+opensnap(char *label, int *mut)
{
char *p, buf[Kvmax];
Tree *t;
+ uint flg;
vlong gen;
Kvp kv;
Key k;
@@ -457,9 +496,11 @@
k.nk = p - buf;
if(btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)) != nil)
return nil;
- if(kv.nv != Snapsz)
- abort();
+ assert(kv.nv == 1+8+4);
gen = UNPACK64(kv.v + 1);
+ flg = UNPACK32(kv.v + 1+8);
+ if(mut != nil)
+ *mut = !!(flg&Lmut);
if((t = mallocz(sizeof(Tree), 1)) == nil)
goto Error;
@@ -539,6 +580,9 @@
Blk *b;
char *p;
+ /* leak it and let the full collection clean it up */
+ if(bp.gen <= t->base)
+ return 0;
if(t == &fs->snap)
dl = &fs->snapdl;
else if((dl = getdl(t->gen, bp.gen)) == nil)