shithub: unionfs

Download patch

ref: f00b8cacec187ab539b5cbbaaa3583051f0ad8e6
parent: 016441e84b12c4b48304ac0ab814a802bf60516e
author: kvik <kvik@a-b.xyz>
date: Wed May 15 23:08:36 EDT 2019

store directory contents in a dynamic array instead of a linked list

this fixes a problem with reading big directories,
where a directory entry got skipped per read response
because of the weird way the walking was done in
tandem with dirread9p(2).
it should be a bit memory friendlier and faster, too.

--- a/unionfs.c
+++ b/unionfs.c
@@ -6,7 +6,7 @@
 
 typedef struct Union Union;
 typedef struct Fil Fil;
-typedef struct Flist Flist;
+typedef struct List List;
 typedef struct Fstate Fstate;
 typedef struct Qidmap Qidmap;
 
@@ -38,15 +38,15 @@
 	char *fspath;	/* internal path */
 };
 
-struct Flist {
-	Fil *file;
-	Flist *next;
+struct List {
+	long n, sz;
+	Fil **l;
 };
 
 struct Fstate {
 	int fd;
 	Fil *file;
-	Flist *dir, *idx;
+	List *flist;
 };
 
 Union u0 = {.next = &u0, .prev = &u0};
@@ -63,11 +63,20 @@
 	if((v = malloc(sz)) == nil)
 		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;
+}
+
 char*
 estrdup(char *s)
 {
@@ -233,34 +242,47 @@
 	free(f);
 }
 
-void
-flistadd(Flist **list, Fil *f)
+List*
+lnew(void)
 {
-	Flist *p;
+	List *l;
 	
-	p = emalloc(sizeof(*p));
-	p->file = f;
-	p->next = *list;
-	*list = p;
+	l = emalloc(sizeof *l);
+	l->n = 0;
+	l->sz = 256;
+	l->l = emalloc(l->sz*sizeof(*l->l));
+
+	return l;
 }
 
 void
-flistfree(Flist *l)
+lfree(List *l)
 {
-	Flist *lp;
+	int i;
 	
-	for(lp = l; lp != nil; l = lp){
-		lp = lp->next;
-		filefree(l->file);
-		free(l);
+	for(i = 0; i < l->n; i++)
+		filefree(l->l[i]);
+}
+
+int
+ladd(List *l, Fil *f)
+{
+	if(l->n == l->sz){
+		l->sz *= 2;
+		l->l = erealloc(l->l, l->sz*sizeof(*l->l));
 	}
+	l->l[l->n++] = f;
+
+	return l->n;
 }
 
 int
-flisthas(Flist *list, char *name)
+lhas(List *l, char *name)
 {
-	for(; list != nil; list = list->next)
-		if(strcmp(list->file->name, name) == 0)
+	int i;
+	
+	for(i = 0; i < l->n; i++)
+		if(strcmp(l->l[i]->name, name) == 0)
 			return 1;
 	return 0;
 }
@@ -281,8 +303,8 @@
 {
 	if(st->file)
 		filefree(st->file);
-	if(st->dir)
-		flistfree(st->dir);
+	if(st->flist)
+		lfree(st->flist);
 	close(st->fd);
 	free(st);
 }
@@ -397,7 +419,7 @@
 	walkandclone(r, walk1, clone, nil);
 }
 
-Flist*
+List*
 filereaddir(Fil *p)
 {
 	int fd;
@@ -406,9 +428,9 @@
 	char *path;
 	Union *u;
 	Fil *f;
-	Flist *list;
+	List *list;
 
-	list = nil;
+	list = lnew();
 	for(u = unionlist->next; u != unionlist; u = u->next){
 		path = mkpath(u->root, p->fspath, nil);
 		if((d = dirstat(path)) == nil){
@@ -425,10 +447,10 @@
 		if(n < 0)
 			continue;
 		for(i = 0; i < n; i++){
-			if(flisthas(list, dir[i].name))
+			if(lhas(list, dir[i].name))
 				continue;
 			f = filenew(&dir[i]);
-			flistadd(&list, f);
+			ladd(list, f);
 		}
 		free(dir);
 	}
@@ -447,10 +469,9 @@
 	st = r->fid->aux;
 	f = st->file;
 
-	if(f->mode&DMDIR){
-		st->dir = filereaddir(f);
-		st->idx = st->dir;
-	}else{
+	if(f->mode&DMDIR)
+		st->flist = filereaddir(f);
+	else{
 		if((st->fd = open(f->path, i->mode)) < 0){
 			responderror(r);
 			return;
@@ -567,17 +588,16 @@
 }
 
 int
-dirgen(int, Dir *dir, void *aux)
+dirgen(int i, Dir *dir, void *aux)
 {
 	Fstate *fs;
-	Flist *l;
+	List *l;
 	
 	fs = aux;
-	l = fs->idx;
-	if(l == nil)
+	l = fs->flist;
+	if(i == l->n)
 		return -1;
-	dirfill(dir, l->file);
-	fs->idx = l->next;
+	dirfill(dir, l->l[i]);
 	return 0;
 }