shithub: gefs

Download patch

ref: f2efe296f67cf1200dfcb30231ddae20dabd27da
parent: fcf497d84bd8740b57f0b20a3982122d292009b3
author: Ori Bernstein <ori@eigenstate.org>
date: Thu Nov 23 14:58:45 EST 2023

fs: implement auto-snapshots

--- a/blk.c
+++ b/blk.c
@@ -1051,10 +1051,6 @@
 	Qent qe;
 	int i;
 
-	if(waserror()){
-		fprint(2, "failed to sync: %s\n", errmsg());
-		nexterror();
-	}
 
 	if(fs->rdonly)
 		return;
@@ -1061,8 +1057,12 @@
 	qlock(&fs->synclk);
 	if(!fs->snap.dirty){
 		qunlock(&fs->synclk);
-		poperror();
 		return;
+	}
+	if(waserror()){
+		fprint(2, "failed to sync: %s\n", errmsg());
+		qunlock(&fs->synclk);
+		nexterror();
 	}
 	flushdlcache(0);
 	gen = aincv(&fs->qgen, 1);
--- a/cons.c
+++ b/cons.c
@@ -57,7 +57,7 @@
 static void
 listsnap(int fd)
 {
-	char *fnam, pfx[Snapsz];
+	char *fmut, *fauto, pfx[Snapsz];
 	Scan s;
 	uint flg;
 	int sz;
@@ -70,8 +70,9 @@
 		if(!btnext(&s, &s.kv))
 			break;
 		flg = UNPACK32(s.kv.v+1+8);
-		fnam = (flg & Lmut) ? "mutable" : "";
-		fprint(fd, "snap %.*s %s\n", s.kv.nk-1, s.kv.k+1, fnam);
+		fmut = (flg & Lmut) ? " mutable" : "";
+		fauto = (flg & Lauto) ? " auto" : "";
+		fprint(fd, "snap %.*s%s%s\n", s.kv.nk-1, s.kv.k+1, fmut, fauto);
 	}
 	btexit(&s);
 }
@@ -89,7 +90,7 @@
 	a->fd = fd;
 	if(ap[0][0] == '-'){
 		switch(ap[0][1]){
-		case 'm':	a->mutable++;	break;
+		case 'm':	a->flag = Lmut;	break;
 		case 'd':	a->delete++;	break;
 		case 'l':
 			listsnap(fd);
--- a/dat.h
+++ b/dat.h
@@ -118,6 +118,7 @@
 
 enum {
 	Lmut	= 1 << 0,
+	Lauto	= 1 << 1,
 };
 
 enum {
@@ -384,8 +385,9 @@
 		struct {	/* AOsnap */
 			char	old[128];
 			char	new[128];
-			int	mutable;
-			int	delete;
+			int	flag;
+			char	delete;
+
 		};
 		struct {	/* AOsync */
 			int	halt;
@@ -565,6 +567,8 @@
 	RWLock	flushq[Nflushtab];
 	int	flushop[Nflushtab];
 
+	char	minutely[60][128];
+	char	hourly[24][128];
 	Stats	stats;
 };
 
--- a/fs.c
+++ b/fs.c
@@ -54,7 +54,8 @@
 		}
 	}
 	if(t == nil && (t = opensnap(a->old, nil)) == nil){
-		fprint(a->fd, "snap: open '%s': does not exist\n", a->old);
+		if(a->fd != -1)
+			fprint(a->fd, "snap: open '%s': does not exist\n", a->old);
 		unlock(&fs->mountlk);
 		poperror();
 		return;
@@ -61,8 +62,10 @@
 	}
 	if(a->delete){
 		if(mnt != nil) {
-			fprint(a->fd, "snap: snap is mounted: '%s'\n", a->old);
+			if(a->fd != -1)
+				fprint(a->fd, "snap: snap is mounted: '%s'\n", a->old);
 			unlock(&fs->mountlk);
+			poperror();
 			return;
 		}
 		if(t->nlbl == 1 && t->nref <= 1 && t->succ == -1){
@@ -74,23 +77,26 @@
 		delsnap(t, t->succ, a->old);
 	}else{
 		if((s = opensnap(a->new, nil)) != nil){
-			fprint(a->fd, "snap: already exists '%s'\n", a->new);
+			if(a->fd != -1)
+				fprint(a->fd, "snap: already exists '%s'\n", a->new);
 			closesnap(s);
 			unlock(&fs->mountlk);
+			poperror();
 			return;
 		}
-		tagsnap(t, a->new, a->mutable);
+		tagsnap(t, a->new, a->flag);
 	}
 	closesnap(t);
 	unlock(&fs->mountlk);
 	poperror();
-	/* we probably want explicit snapshots to get synced */
-	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);
+	if(a->fd != -1){
+		if(a->delete)
+			fprint(a->fd, "deleted: %s\n", a->old);
+		else if(a->flag & Lmut)
+			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
@@ -1407,6 +1413,7 @@
 		return;
 	}
 	lock(f);
+
 	if(waserror()){
 		rerror(m, errmsg());
 		goto Err;
@@ -1652,8 +1659,7 @@
 			wunlock(f->dent);
 			nexterror();
 		}
-		if((*ao = malloc(sizeof(Amsg))) == nil)
-			error(Enomem);
+		*ao = emalloc(sizeof(Amsg), 1);
 		aincl(&f->mnt->ref, 1);
 		(*ao)->op = AOclear;
 		(*ao)->mnt = f->mnt;
@@ -2231,6 +2237,7 @@
 				fprint(2, "taking snap: %s\n", errmsg());
 				ainc(&fs->rdonly);
 			}
+
 			qlock(&fs->mutlk);
 			if(waserror()){
 				qunlock(&fs->mutlk);
@@ -2242,6 +2249,7 @@
 			epochend(id);
 			poperror();
 			qunlock(&fs->mutlk);
+
 			if(pred != -1){
 				epochwait();
 				sweeptree(bp, pred);
@@ -2278,22 +2286,107 @@
 	}
 }
 
+static void
+loadautos(void)
+{
+	char pfx[Snapsz];
+	int m, h, ns, nl;
+	uint flg;
+	Scan s;
+
+	m = 0;
+	h = 0;
+	pfx[0] = Klabel;
+	pfx[1] = '@';
+	btnewscan(&s, pfx, 2);
+	btenter(&fs->snap, &s);
+	while(1){
+		if(!btnext(&s, &s.kv))
+			break;
+		flg = UNPACK32(s.kv.v+1+8);
+		if(!(flg & Lauto))
+			continue;
+		nl = s.kv.nk-1;
+		ns = strlen("@minute.");
+		assert(nl+1 < sizeof(fs->minutely[0]));
+		if(nl > ns && strncmp(s.kv.k+1, "@minute.", ns) == 0){
+			memcpy(fs->minutely[m], s.kv.k+1, s.kv.nk-1);
+			fs->minutely[m][s.kv.nk-1] = 0;
+			m = (m+1)%60;
+			continue;
+		}
+		ns = strlen("@hour.");
+		if(nl > ns && strncmp(s.kv.k+1, "@hour.", ns) == 0){
+			memcpy(fs->hourly[h], s.kv.k+1, s.kv.nk-1);
+			fs->hourly[h][s.kv.nk-1] = 0;
+			h = (h+1)%24;
+			continue;
+		}
+		fprint(2, "unknown autosnap %.*s\n", s.kv.nk-1, s.kv.k+1);
+	}
+	btexit(&s);
+}
+
 void
+snapmsg(char *old, char *new, int flg)
+{
+	Amsg *a;
+
+	a = emalloc(sizeof(Amsg), 1);
+	a->op = AOsnap;
+	a->fd = -1;
+	a->flag = flg;
+	strecpy(a->old, a->old+sizeof(a->old), old);
+	if(new == nil)
+		a->delete = 1;
+	else
+		strecpy(a->new, a->new+sizeof(a->new), new);
+	chsend(fs->admchan, a);
+}
+
+void
 runtasks(int, void *)
 {
+	char buf[128];
+	Tm now, then;
+	int m, h;
 	Amsg *a;
 
+	m = 0;
+	h = 0;
+	loadautos();
+	tmnow(&then, nil);
+	tmnow(&now, nil);
 	while(1){
+		if(waserror())
+			fprint(2, "task error: %s", errmsg());
 		sleep(5000);
-		a = mallocz(sizeof(Amsg), 1);
-		if(a == nil){
-			fprint(2, "alloc sync msg: %r\n");
-			free(a);
-			return;
-		}
+		a = emalloc(sizeof(Amsg), 1);
 		a->op = AOsync;
 		a->halt = 0;
 		a->fd = -1;
 		chsend(fs->admchan, a);
+
+		tmnow(&now, nil);
+		if(now.yday != then.yday){
+			snprint(buf, sizeof(buf), "@day.%τ", tmfmt(&now, "YYYY.MM.DD[_]hh:mm:ss"));
+			snapmsg("main", buf, Lauto);
+		}
+		if(now.hour != then.hour){
+			if(fs->hourly[h][0] != 0)
+				snapmsg(fs->hourly[h], nil, 0);
+			snprint(fs->hourly[h], sizeof(fs->hourly[h]), "@hour.%τ", tmfmt(&now, "YYYY.MM.DD[_]hh:mm:ss"));
+			snapmsg("main", fs->hourly[h], Lauto);
+			h = (h+1)%24;
+		}
+		if(now.min != then.min){
+			if(fs->minutely[m][0] != 0)
+				snapmsg(fs->minutely[m], nil, 0);
+			snprint(fs->minutely[m], sizeof(fs->minutely[m]), "@minute.%τ", tmfmt(&now, "YYYY.MM.DD[_]hh:mm:ss"));
+			snapmsg("main", fs->minutely[m], Lauto);
+			m = (m+1)%60;
+		}
+		then = now;
+		poperror();
 	}
 }
--- a/main.c
+++ b/main.c
@@ -265,6 +265,7 @@
 	errctx = privalloc();
 	if((*errctx = mallocz(sizeof(Errctx), 1)) == nil)
 		sysfatal("malloc: %r");
+	tmfmtinstall();
 	fmtinstall('H', encodefmt);
 	fmtinstall('B', Bconv);
 	fmtinstall('M', Mconv);
--- a/snap.c
+++ b/snap.c
@@ -109,8 +109,8 @@
 	m.op = Oinsert;
 	dlist2kv(dl, &m, kvbuf, sizeof(kvbuf));
 	btupsert(&fs->snap, &m, 1);
-	poperror();
 Found:
+	poperror();
 	h = ihash(gen) ^ ihash(bgen);
 	p = &fs->dlcache[h % fs->dlcmax];
 	dl->chain = *p;
@@ -325,7 +325,7 @@
  * will show up in the dump.
  */
 void
-tagsnap(Tree *t, char *name, int mutable)
+tagsnap(Tree *t, char *name, int flg)
 {
 	char buf[3][Kvmax];
 	Msg m[3];
@@ -343,7 +343,7 @@
 		free(n);
 		nexterror();
 	}
-	if(mutable){
+	if(flg & Lmut){
 		n = emalloc(sizeof(Tree), 1);
 		n->memref = 1;
 		n->dirty = 0;
@@ -362,7 +362,7 @@
 		retag2kv(t->gen, 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]));
+		lbl2kv(name, n->gen, flg, &m[i], buf[i], sizeof(buf[i]));
 		i++;
 		m[i].op = Oinsert;
 		tree2kv(n, &m[i], buf[i], sizeof(buf[i]));
@@ -376,7 +376,7 @@
 		m[i].op = Oinsert;
 		t->pred = t->gen;
 		t->nlbl++;
-		lbl2kv(name, t->gen, 0, &m[i], buf[i], sizeof(buf[i]));
+		lbl2kv(name, t->gen, flg, &m[i], buf[i], sizeof(buf[i]));
 		i++;
 	}
 	btupsert(&fs->snap, m, i);
--- a/tree.c
+++ b/tree.c
@@ -1041,7 +1041,6 @@
 			enqueue(p->nl);
 			rp = p;
 		}else{
-
 			splitleaf(t, up, p, &mid);
 			enqueue(p->nl);
 			enqueue(p->nr);
@@ -1218,12 +1217,13 @@
 	npull = 0;
 	path = nil;
 	npath = 0;
+
+Again:
 	if(waserror()){
 		freepath(t, path, npath);
 		nexterror();
 	}
 
-Again:
 	b = getroot(t, &height);
 	if(b->type == Tpivot && !filledbuf(b, nmsg, sz)){
 		fastupsert(t, b, msg, nmsg);
@@ -1230,7 +1230,6 @@
 		poperror();
 		return;
 	}
-
 	/*
 	 * The tree can grow in height by 1 when we
 	 * split, so we allocate room for one extra
@@ -1279,7 +1278,6 @@
 	else
 		fatal("broken path change");
 
-
 	assert(rb->bp.addr != 0);
 	assert(rb->bp.addr != 0);
 
@@ -1288,11 +1286,13 @@
 	t->bp = rb->bp;
 	t->dirty = 1;
 	unlock(&t->lk);
+
 	npull += rp->npull;
 	freepath(t, path, npath);
+	poperror();
+
 	if(npull != nmsg)
 		goto Again;
-	poperror();
 }
 
 Blk*