shithub: unionfs

Download patch

ref: df0f9b75a74c4fc2241bc1a85e605aa080d71bfd
parent: f784be8431711c15cbc1f0a60168303a6764405d
author: kvik <kvik@a-b.xyz>
date: Tue May 14 13:50:51 EDT 2019

implement shadow tree for file creation

previously, creation of new files was first
attempted in the branch marked for creation (-c
flag) and if that couldn't happen (because of the
path leading to the file not existing in the branch)
the union list was searched in-order until the path
was found, and the file was created there.

this was good enough for the most basic usages, like
the unioning of /bin, in which case if could be
considered useful to route new files into matching
branches; but this behaviour can have unpredictable
results and requires somewhat careful thought to use
advantageously.

forcing the creation of new files in the shadow
tree, i.e.  the one marked with a -c flag, is an
intuitive, easy to understand concept that is also a
very useful default for the kind of usage this
file server was intended for.  going back to our
most basic example of /bin, the user of unionfs will
probably want all the new files and directories created
under /bin to end up in his $home/bin/rc directory,
instead of getting sprinkled out over the system paths.
another very useful usage scenario enabled by this
is build overlays, where the build artifacts can be
transparently collected into a ramfs for improved
speed and ease of cleanup.

this change neccesitates thinking about and eventually
adding a copy-on-write scheme for file modifications,
which would facilitate experimenting with read-only
9front live images with persistable overlays, or
something... :)

--- a/unionfs.c
+++ b/unionfs.c
@@ -468,6 +468,39 @@
 	respond(r, nil);
 }
 
+int
+mkdirp(char *path)
+{
+	int fd;
+	char *p;
+	Dir *d;
+	
+	assert(path != nil);
+	if((d = dirstat(path)) != nil){
+		free(d);
+		return 1;
+	}
+	path = p = strdup(path);
+	for(; p != nil ;){
+		if(p[0] == '/')
+			p++;
+		if(p = strchr(p, '/'))
+			*p = 0;
+		if((d = dirstat(path)) == nil){
+			if((fd = create(path, 0, 0777|DMDIR)) < 0){
+				free(path);
+				return -1;
+			}
+			close(fd);
+		}
+		free(d);
+		if(p != nil)
+			*p++ = '/';
+	}
+	free(path);
+	return 1;
+}
+
 void
 fscreate(Req *r)
 {
@@ -486,27 +519,11 @@
 	for(u = unionlist->next; u != unionlist; u = u->next)
 		if(u->create == 1)
 			break;
-	assert(u != unionlist);
 	path = mkpath(u->root, f->fspath, nil);
-	d = dirstat(path);
-	if(d != nil){
-		free(d);
-		goto work;
+	if(mkdirp(path) < 0){
+		responderror(r);
+		return;
 	}
-	for(u = unionlist->next; u != unionlist; u = u->next){
-		if(u->create == 1)
-			continue;
-		free(path);
-		path = mkpath(u->root, f->fspath, nil);
-		d = dirstat(path);
-		if(d != nil){
-			free(d);
-			goto work;
-		}
-	}
-	sysfatal("something's fucked");
-	
-work:
 	npath = mkpath(path, i->name, nil);
 	free(path);
 	st = emalloc(sizeof(*st));