shithub: unionfs

Download patch

ref: cf7743f62d8752a9a4e33f3a8265f57c6e23290f
parent: 42ba007a8b005e56033b05fa1385670472f0a339
author: kvik <kvik@a-b.xyz>
date: Sat May 29 07:41:48 EDT 2021

Rewrite and simplify the whole lot

Directory reads are done using union binds which allows
removal of a bunch of code.
Fid state management is simplified. Instead of refcounting
clones etc. we just copy everything, which makes things
easier to reason about and removes the need to introduce
locking for FILE structs.
Various other minor improvements are included as well.

--- a/unionfs.c
+++ b/unionfs.c
@@ -5,316 +5,322 @@
 #include <9p.h>
 #include "unionfs.h"
 
+Srv thefs;
 Branch *branch;
 usize nbranch;
-F *root;
-Srv thefs;
+QLock mtptlock;
+Mtpt *mtpt;
 
-F*
-filenew(Dir *d)
+Mtpt*
+mtptgrab(void)
 {
-	F *f;
+	static int mtptnext = 0;
+	Mtpt *m;
 
-	f = emalloc(sizeof(*f));
-	f->ref = 1;
-	f->Dir = *d;
-	f->qid = qencode(d);
-	f->name = estrdup(d->name);
-	f->uid = estrdup(d->uid);
-	f->gid = estrdup(d->gid);
-	f->muid = estrdup(d->muid);
-	return f;
+	qlock(&mtptlock);
+	if(mtpt == nil){
+		mtpt = emalloc(sizeof(Mtpt));
+		mtpt->path = smprint("/mnt/mtpt%d", mtptnext++);
+	}
+	m = mtpt;
+	mtpt = m->next;
+	qunlock(&mtptlock);
+	unmount(nil, m->path);
+	return m;
 }
 
 void
-filefree(F *f)
+mtptfree(Mtpt *m)
 {
-	if(f == root)
-		return;
-	if(decref(f))
-		return;
-	free(f->name);
-	free(f->uid);
-	free(f->gid);
-	free(f->muid);
-	free(f->path);
-	free(f->fspath);
-	free(f);
+	qlock(&mtptlock);
+	m->next = mtpt;
+	mtpt = m;
+	qunlock(&mtptlock);
 }
 
-uint
-fthash(char *s)
+FILE*
+filenew(void)
 {
-	uint h;
-	for(h = 0; *s; s++)
-		h = *s + 31*h;
-	return h % Nftab;
-}
+	FILE *f;
 
-Ftab*
-ftnew(void)
-{
-	int i;
-	Ftab *ft, *p;
-	
-	ft = emalloc(Nftab*sizeof(Ftab));
-	for(i = 0; i < Nftab; i++){
-		p = &ft[i];
-		p->sz = Nftlist;
-		p->l = emalloc(p->sz*sizeof(*p->l));
-	}
-	return ft;
+	f = emalloc(sizeof(FILE));
+	f->fd = -1;
+	return f;
 }
 
 void
-ftfree(Ftab *ft)
+filefree(FILE *f)
 {
-	int i, j;
-	Ftab *p;
-	
-	for(i = 0; i < Nftab; i++){
-		p = &ft[i];
-		for(j = 0; j < p->n; j++)
-			filefree(p->l[j]);
-		free(p->l);
-	}
-	free(ft);
+	if(f == nil)
+		return;
+	if(f->name) free(f->name);
+	if(f->uid) free(f->uid);
+	if(f->gid) free(f->gid);
+	if(f->muid) free(f->muid);
+	if(f->path) free(f->path);
+	if(f->realpath) free(f->realpath);
+	if(f->fd != -1) close(f->fd);
+	if(f->dirs) free(f->dirs);
+	if(f->mtpt) mtptfree(f->mtpt);
+	free(f);
 }
 
 void
-ftadd(Ftab *ft, F *f)
+dircopy(Dir *a, Dir *b)
 {
-	Ftab *p;
-	
-	p = &ft[fthash(f->name)];
-	if(p->n == p->sz){
-		p->sz *= 2;
-		p->l = erealloc(p->l, p->sz*sizeof(*p->l));
-	}
-	p->l[p->n++] = f;
+	a->type = b->type;
+	a->dev = b->dev;
+	a->qid = b->qid;
+	a->mode = b->mode;
+	a->mtime = b->mtime;
+	a->atime = b->atime;
+	a->name = estrdup(b->name);
+	a->uid = estrdup(b->uid);
+	a->gid = estrdup(b->gid);
+	a->muid = estrdup(b->muid);
 }
 
-int
-fthas(Ftab *ft, char *name)
+void
+fsattach(Req *r)
 {
-	int i;
-	Ftab *p;
+	FILE *f;
+	char *user;
 	
-	p = &ft[fthash(name)];
-	for(i = 0; i < p->n; i++)
-		if(strcmp(p->l[i]->name, name) == 0)
-			return 1;
-	return 0;
-}
-
-F*
-ftidx(Ftab *ft, long i)
-{
-	long y;
-	Ftab *p;
+	f = filenew();
+	f->name = estrdup("/");
+	f->mode = 0777|DMDIR;
+	user = getuser();
+	f->uid = estrdup(user);
+	f->gid = estrdup(user);
+	f->muid = estrdup(user);
+	f->mtime = f->atime = time(0);
+	f->type = 0xFFFFFFFF;
+	f->dev = 0xFFFFFFFFFFFFFFFF;
+	f->qid = (Qid){0, 0, QTDIR};
+	f->qid = qencode(f);
+	f->path = estrdup(f->name);
+	f->realpath = estrdup(f->name);
 	
-	for(y = 0; y < Nftab; y++){
-		p = &ft[y];
-		if(p->n == 0)
-			continue;
-		if(i >= p->n){
-			i -= p->n;
-			continue;
-		}
-		return p->l[i];
-	}
-	return nil;
+	r->fid->aux = f;
+	r->fid->qid = f->qid;
+	r->ofcall.qid = f->qid;
+	respond(r, nil);
 }
 
-Fstate*
-fstatenew(F *f)
+char*
+mkpath(char *a0, ...)
 {
-	Fstate *st;
-	
-	st = emalloc(sizeof(*st));
-	st->fd = -1;
-	st->file = (F*)copyref(f);
-	return st;
-}
+	va_list args;
+	int i;
+	char *a;
+	char *ap[] = {a0, "", ""};
 
-void
-fstatefree(Fstate *st)
-{
-	if(st->file)
-		filefree(st->file);
-	if(st->ftab)
-		ftfree(st->ftab);
-	close(st->fd);
-	free(st);
-}
+	va_start(args, a0);
+	for(i = 1; (a = va_arg(args, char*)) != nil && i < 3; i++)
+		ap[i] = a;
+	va_end(args);
+	if((a = smprint("%s/%s/%s", ap[0], ap[1], ap[2])) == nil)
+		sysfatal("smprint: %r");
 
-void
-initroot(void)
-{
-	char *user;
-	Dir d;
-	
-	nulldir(&d);
-	d.qid = (Qid){0, 0, QTDIR};
-	d.name = ".";
-	d.mode = 0777|DMDIR;
-	user = getuser();
-	d.uid = user;
-	d.gid = user;
-	d.muid = user;
-	d.mtime = time(0);
-	d.atime = time(0);
-	d.length = 0;
-	
-	root = filenew(&d);
-	root->fspath = estrdup(d.name);
-	root->path = estrdup(d.name);
+	return cleanname(a);
 }
 
-void
-fsattach(Req *r)
+char*
+clone(Fid *fid, Fid *newfid, void*)
 {
-	Fstate *st;
+	FILE *f;
+	FILE *parent = fid->aux;
 	
-	st = fstatenew(root);
-	r->fid->aux = st;
-	r->fid->qid = root->qid;
-	r->ofcall.qid = root->qid;
-	respond(r, nil);
+	f = filenew();
+	dircopy(f, parent);
+	f->qid = parent->qid;
+	f->path = estrdup(parent->path);
+	f->realpath = estrdup(parent->realpath);
+	newfid->aux = f;
+	return nil;
 }
 
-F*
-filewalk(F *p, char *name)
+char*
+walkto(Fid *fid, char *name, void *aux)
 {
-	char *path, *np;
+	Req *r;
 	Dir *d;
-	F *f;
+	FILE *f;
+	char *path, *realpath;
+	int i, *nwalk;
 	
-	np = mkpath(p->fspath, name, nil);
-	for(int i = 0; i < nbranch; i++){
-		path = mkpath(branch[i].root, np, nil);
-		if((d = dirstat(path)) == nil){
-			free(path);
+	r = aux;
+	f = fid->aux;
+	nwalk = r->aux;
+	path = mkpath(f->path, name, nil);
+	for(i = 0; i < nbranch; i++){
+		realpath = mkpath(branch[i].root, path, nil);
+		if((d = dirstat(realpath)) == nil)
 			continue;
+		if(*nwalk == r->ifcall.nwname){
+			filefree(f);
+			f = filenew();
+			dircopy(f, d);
+		}else{
+			free(f->path);
+			free(f->realpath);
 		}
-		f = filenew(d);
+		f->qid = qencode(d);
 		free(d);
-		f->fspath = np;
 		f->path = path;
-		filefree(p);
-		return f;
+		f->realpath = realpath;
+		fid->aux = f;
+		fid->qid = f->qid;
+		*nwalk = *nwalk + 1;
+		return nil;
 	}
-	free(np);
-	return nil;
+	return "not found";
 }
 
-char*
-walk1(Fid *fid, char *name, void *)
+void
+fswalk(Req *r)
 {
-	F *p, *f;
-	Fstate *st;
+	int nwalk;
 
-	st = fid->aux;
-	p = st->file;
-	if((f = filewalk(p, name)) == nil)
-		return "not found";
-	st->file = f;
-
-	fid->qid = f->qid;
-	return nil;
+	nwalk = 1;
+	r->aux = &nwalk;
+	walkandclone(r, walkto, clone, r);
 }
 
-char*
-clone(Fid *old, Fid *new, void*)
-{
-	Fstate *fs;
-
-	fs = old->aux;
-	new->aux = fstatenew(fs->file);
-	return nil;
-}
-
 void
 destroyfid(Fid *fid)
 {
 	if(fid->aux)
-		fstatefree(fid->aux);
+		filefree(fid->aux);
 	fid->aux = nil;
 }
 
 void
-fswalk(Req *r)
+fsopen(Req *r)
 {
+	Fcall *T, *R;
+	FILE *f;
+	usize i;
+	char *path;
+	Dir *d;
+	
+	T = &r->ifcall;
+	R = &r->ofcall;
+	f = r->fid->aux;
+
 	srvrelease(&thefs);
-	walkandclone(r, walk1, clone, nil);
+	if(f->mode & DMDIR){
+		f->mtpt = mtptgrab();
+		for(i = 0; i < nbranch; i++){
+			path = mkpath(branch[i].root, f->path, nil);
+			if((d = dirstat(path)) != nil){
+				if(d->mode & DMDIR)
+				if(bind(path, f->mtpt->path, MAFTER) == -1)
+					sysfatal("bind: %r");
+				free(d);
+			}
+			free(path);
+		}
+		if((f->fd = open(f->mtpt->path, T->mode)) < 0){
+			responderror(r);
+			goto done;
+		}
+	}else{
+		if((f->fd = open(f->realpath, T->mode)) < 0){
+			responderror(r);
+			goto done;
+		}
+	}
+	R->iounit = iounit(f->fd);
+	respond(r, nil);
+done:
 	srvacquire(&thefs);
 }
 
-Ftab*
-filereaddir(F *p)
+void
+fsremove(Req *r)
 {
-	int fd;
-	long n;
-	Dir *dir, *d;
-	char *path;
-	F *f;
-	Ftab *ft;
+	FILE *f;
+	
+	f = r->fid->aux;
+	srvrelease(&thefs);
+	if(remove(f->path) < 0){
+		responderror(r);
+		goto done;
+	}
+	respond(r, nil);
+done:
+	srvacquire(&thefs);
+}
 
-	ft = ftnew();
-	for(usize i = 0; i < nbranch; i++){
-		path = mkpath(branch[i].root, p->fspath, nil);
-		if((d = dirstat(path)) == nil){
-		err:
-			free(path);
-			continue;
+int
+dirgen(int i, Dir *d, void *aux)
+{
+	FILE *f = aux;
+	
+	if(i == 0){
+		if(f->dirs){
+			free(f->dirs);
+			f->dirs = nil;
+			f->ndirs = 0;
 		}
-		free(d);
-		if((fd = open(path, OREAD)) < 0)
-			goto err;
-		free(path);
-		while((n = dirread(fd, &dir)) > 0){
-			for(usize j = 0; j < n; j++){
-				if(i > 0 && fthas(ft, dir[j].name))
-					continue;
-				f = filenew(&dir[j]);
-				ftadd(ft, f);
-			}
-			free(dir);
-		}
-		if(n < 0)
-			fprint(2, "dirread: %r\n");
-		close(fd);
+		f->ndirs = dirreadall(f->fd, &f->dirs);
+		if(f->ndirs == -1)
+			sysfatal("dirreadall: %r");
 	}
-	return ft;
+	if(f->ndirs == i)
+		return -1;
+	dircopy(d, &f->dirs[i]);
+	d->qid = qencode(d);
+	return 0;
 }
 
 void
-fsopen(Req *r)
+fsread(Req *r)
 {
+	long n;
 	Fcall *T, *R;
-	Fstate *st;
-	F *f;
+	FILE *f;
 	
 	T = &r->ifcall;
 	R = &r->ofcall;
-	st = r->fid->aux;
-	f = st->file;
+	f = r->fid->aux;
 
 	srvrelease(&thefs);
-	if(f->mode&DMDIR)
-		st->ftab = filereaddir(f);
-	else{
-		if((st->fd = open(f->path, T->mode)) < 0){
+	if(f->mode&DMDIR){
+		dirread9p(r, dirgen, f);
+	}else{
+		if((n = pread(f->fd, R->data, T->count, T->offset)) < 0){
 			responderror(r);
-			srvacquire(&thefs);
-			return;
+			goto done;
 		}
-		R->iounit = iounit(st->fd);
+		r->ofcall.count = n;
 	}
 	respond(r, nil);
+done:
 	srvacquire(&thefs);
 }
 
+void
+fswrite(Req *r)
+{
+	Fcall *T, *R;
+	FILE *f;
+	
+	T = &r->ifcall;
+	R = &r->ofcall;
+	f = r->fid->aux;
+	
+	srvrelease(&thefs);
+	if((R->count = pwrite(f->fd, T->data, T->count, T->offset)) != T->count){
+		responderror(r);
+		goto done;
+	}
+	respond(r, nil);
+done:
+	srvacquire(&thefs);
+}
+
 int
 mkdirp(char *path)
 {
@@ -351,155 +357,61 @@
 void
 fscreate(Req *r)
 {
-	char *path, *npath;
+	char *path, *realpath;
 	usize i;
 	Dir *d;
 	Fcall *T, *R;
-	Fstate *st;
-	F *f, *nf;
+	FILE *parent, *f;
+	int fd;
 	
 	T = &r->ifcall;
 	R = &r->ofcall;
-	st = r->fid->aux;
-	f = st->file;
+	parent = r->fid->aux;
 	
 	srvrelease(&thefs);
 	for(i = 0; i < nbranch; i++)
 		if(branch[i].create == 1)
 			break;
-	path = mkpath(branch[i].root, f->fspath, nil);
+	path = mkpath(branch[i].root, parent->path, nil);
 	if(mkdirp(path) < 0){
 		responderror(r);
-		srvacquire(&thefs);
-		return;
+		goto done;
 	}
-	npath = mkpath(path, T->name, nil);
+	realpath = mkpath(path, T->name, nil);
 	free(path);
-	st = emalloc(sizeof(*st));
-	if((st->fd = create(npath, T->mode, T->perm)) < 0){
+	if((fd = create(realpath, T->mode, T->perm)) < 0){
+		free(realpath);
 		responderror(r);
-		srvacquire(&thefs);
-		return;
+		goto done;
 	}
-	if((d = dirfstat(st->fd)) == nil){
-		fstatefree(st);
+	if((d = dirfstat(fd)) == nil){
+		free(realpath);
 		responderror(r);
-		srvacquire(&thefs);
-		return;
+		goto done;
 	}
-	nf = filenew(d);
+	f = filenew();
+	dircopy(f, d);
+	f->fd = fd;
+	f->qid = qencode(d);
+	f->path = mkpath(parent->path, T->name, nil);
+	f->realpath = realpath;
 	free(d);
-	nf->path = npath;
-	nf->fspath = estrdup(f->fspath);
-	st->file = nf;
+	filefree(parent);
 	
-	r->fid->aux = st;
-	r->fid->qid = nf->qid;
-	R->qid = nf->qid;
+	r->fid->aux = f;
+	R->qid = f->qid;
 	respond(r, nil);
+done:
 	srvacquire(&thefs);
 }
 
-void
-fsremove(Req *r)
-{
-	Fstate *st;
-	F *f;
-	
-	st = r->fid->aux;
-	f = st->file;
-	srvrelease(&thefs);
-	if(remove(f->path) < 0){
-		responderror(r);
-		srvacquire(&thefs);
-		return;
-	}
-	respond(r, nil);
-	srvacquire(&thefs);
-}
 
 void
-dirfill(Dir *dir, F *f)
-{
-	*dir = f->Dir;
-	dir->qid = f->qid;
-	dir->name = estrdup(f->name);
-	dir->uid = estrdup(f->uid);
-	dir->gid = estrdup(f->gid);
-	dir->muid = estrdup(f->muid);
-}
-
-int
-dirgen(int i, Dir *dir, void *aux)
-{
-	Fstate *fs;
-	F *f;
-	
-	fs = aux;
-	f = ftidx(fs->ftab, i);
-	if(f == nil)
-		return -1;
-	dirfill(dir, f);
-	return 0;
-}
-
-void
-fsread(Req *r)
-{
-	long n;
-	Fcall *T, *R;
-	F *f;
-	Fstate *st;
-	
-	T = &r->ifcall;
-	R = &r->ofcall;
-	st = r->fid->aux;
-	f = st->file;
-
-	srvrelease(&thefs);
-	if(f->mode&DMDIR){
-		dirread9p(r, dirgen, st);
-		respond(r, nil);
-		srvacquire(&thefs);
-		return;
-	}
-	if((n = pread(st->fd, R->data, T->count, T->offset)) < 0){
-		responderror(r);
-		srvacquire(&thefs);
-		return;
-	}
-	r->ofcall.count = n;
-	respond(r, nil);
-	srvacquire(&thefs);
-}
-
-void
-fswrite(Req *r)
-{
-	Fcall *T, *R;
-	Fstate *fs;
-	
-	T = &r->ifcall;
-	R = &r->ofcall;
-	fs = r->fid->aux;
-	
-	srvrelease(&thefs);
-	if((R->count = pwrite(fs->fd, T->data, T->count, T->offset)) != T->count){
-		responderror(r);
-		srvacquire(&thefs);
-		return;
-	}
-	respond(r, nil);
-	srvacquire(&thefs);
-}
-
-void
 fsstat(Req *r)
 {
-	Fstate *st;
+	FILE *f = r->fid->aux;
 	
-	st = r->fid->aux;
-	dirfill(&r->d, st->file);
+	dircopy(&r->d, f);
 	respond(r, nil);
 }
 
@@ -506,19 +418,15 @@
 void
 fswstat(Req *r)
 {
-	Fstate *st;
-	F *f;
+	FILE *f = r->fid->aux;
 	
-	st = r->fid->aux;
-	f = st->file;
-	
 	srvrelease(&thefs);
-	if(dirwstat(f->path, &r->d) < 0){
+	if(dirwstat(f->realpath, &r->d) < 0){
 		responderror(r);
-		srvacquire(&thefs);
-		return;
+		goto done;
 	}
 	respond(r, nil);
+done:
 	srvacquire(&thefs);
 }
 
@@ -539,13 +447,13 @@
 main(int argc, char *argv[])
 {
 	int c, i, mflag, stdio;
-	char *mtpt, *srvname, *path, *p;
+	char *mountat, *srvname, *path, *p;
 	Dir *d;
 	Branch *b;
 
 	c = 0;
 	mflag = MREPL|MCREATE;
-	mtpt = nil;
+	mountat = nil;
 	srvname = nil;
 	stdio = 0;
 	ARGBEGIN{
@@ -565,7 +473,7 @@
 		chatty9p++;
 		break;
 	case 'm':
-		mtpt = EARGF(usage());
+		mountat = EARGF(usage());
 		break;
 	case 's':
 		srvname = EARGF(usage());
@@ -578,8 +486,8 @@
 	}ARGEND;
 	if(argc < 1)
 		usage();
-	if((mtpt || srvname) == 0)
-		mtpt = "/mnt/union";
+	if((mountat || srvname) == 0)
+		mountat = "/mnt/union";
 	nbranch = argc;
 	branch = b = emalloc(nbranch * sizeof(Branch));
 	for(i = 0; i < argc; i++){
@@ -596,7 +504,7 @@
 			continue;
 		}
 		free(d);
-		if(mtpt && strcmp(path, mtpt) == 0){
+		if(mountat && strcmp(path, mountat) == 0){
 			p = pivot(path);
 			free(path);
 			path = p;
@@ -620,9 +528,8 @@
 	thefs.stat = fsstat;
 	thefs.wstat = fswstat;
 	thefs.destroyfid = destroyfid;
-	initroot();
 	if(stdio == 0){
-		postmountsrv(&thefs, srvname, mtpt, mflag);
+		postmountsrv(&thefs, srvname, mountat, mflag);
 		exits(nil);
 	}
 	thefs.infd = 0;
--- a/unionfs.h
+++ b/unionfs.h
@@ -1,12 +1,6 @@
-enum {
-	Nftab = 32,
-	Nftlist = 32,
-};
-
 typedef struct Branch Branch;
-typedef struct F F;
-typedef struct Ftab Ftab;
-typedef struct Fstate Fstate;
+typedef struct FILE FILE;
+typedef struct Mtpt Mtpt;
 
 struct Branch {
 	char *root;
@@ -13,28 +7,24 @@
 	int create;
 };
 
-struct F {
-	Ref;
+struct FILE {
 	Dir;
-	char *path;   /* real path */
-	char *fspath; /* internal path */
-};
+	char *realpath;
+	char *path;
 
-struct Ftab {
-	long n, sz;
-	F **l;
+	int fd;
+	Mtpt *mtpt;
+	Dir *dirs;
+	long ndirs;
 };
 
-struct Fstate {
-	int fd;
-	F *file;
-	Ftab *ftab;
+struct Mtpt {
+	char *path;
+	Mtpt *next;
 };
 
 void usage(void);
 Qid qencode(Dir*);
 char *mkpath(char*, ...);
-Ref *copyref(Ref*);
 void *emalloc(ulong);
-void *erealloc(void*, ulong);
 char *estrdup(char*);
--- a/util.c
+++ b/util.c
@@ -10,35 +10,6 @@
 	exits("usage");
 }
 
-char*
-mkpath(char *a0, ...)
-{
-	va_list args;
-	int i;
-	char *a;
-	char *ap[] = {a0, "", ""};
-
-	va_start(args, a0);
-	for(i = 1; (a = va_arg(args, char*)) != nil && i < 3; i++)
-		ap[i] = a;
-	va_end(args);
-	if((a = smprint("%s/%s/%s", ap[0], ap[1], ap[2])) == nil)
-		sysfatal("smprint: %r");
-
-	return cleanname(a);
-}
-
-Ref*
-copyref(Ref *r)
-{
-	incref(r);
-	return r;
-}
-
-/*
- * Error-checked library functions
- */
-
 void*
 emalloc(ulong sz)
 {
@@ -48,15 +19,6 @@
 		sysfatal("emalloc: %r");
 	memset(v, 0, sz);
 	setmalloctag(v, getcallerpc(&sz));
-	return v;
-}
-
-void*
-erealloc(void *v, ulong sz)
-{
-	if((v = realloc(v, sz)) == nil && sz != 0)
-		sysfatal("realloc: %r");
-	setrealloctag(v, getcallerpc(&v));
 	return v;
 }