code: plan9front

Download patch

ref: a2ebe5c79a45a0f9ed35bbb690f5070cdfded3ad
parent: 4a83ce37c649dbcfb5a87c022aad626226904363
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed Mar 31 13:49:10 EDT 2021

devfs: fix locking and ignore undocumented "fsdev:\n" configuration signature

The confstr was shared between readers so seprintconf() could
write concurrently to that buffer which is not safe.

This replaces the shared static confstr[Maxconf] buffer with a
pointer that is initially nil and a buffer that is alloced on
demand.

The new confstr pointer (and buffer) is now only updated while
wlock()ed from the new setconfstr() function.

This is now done by mconfig() / mdelctl() just before releasing
the wlock.

Now, rdconf() will check if confstr has been initialized, and
test for it again while wlock()ed; making sure the configuration
is read only once.

Also, rdconf() used to check for a undocumented "fsdev:\n" string
at the beginning of config data tho that was never documented.

This changes mconfig() to ignore that particular signature so
the example from the manpage will work as documented.

--- a/sys/src/9/port/devfs.c
+++ b/sys/src/9/port/devfs.c
@@ -117,12 +117,10 @@
 static char *disk;		/* default tree name used */
 static char *source;		/* default inner device used */
 static int sectorsz = Sectorsz;	/* default sector size */
-static char confstr[Maxconf];	/* textual configuration */
+static char *confstr;		/* textual configuration */
 
 static int debug;
 
-static char cfgstr[] = "fsdev:\n";
-
 static Qid tqid = {Qtop, 0, QTDIR};
 static Qid cqid = {Qctl, 0, 0};
 
@@ -180,6 +178,36 @@
 	return s;
 }
 
+static char*
+seprintconf(char *s, char *e)
+{
+	int	i, j;
+	Tree	*t;
+
+	*s = 0;
+	for(i = 0; i < ntrees; i++){
+		t = trees[i];
+		if(t != nil)
+			for(j = 0; j < t->nadevs; j++)
+				if(t->devs[j] != nil)
+					s = seprintdev(s, e, t->devs[j]);
+	}
+	return s;
+}
+
+/* called with lck w */
+static void
+setconfstr(void)
+{
+	char *s;
+
+	s = confstr;
+	if(s == nil)
+		s = smalloc(Maxconf);
+	seprintconf(s, s+Maxconf);
+	confstr = s;
+}
+
 static vlong
 mkpath(int tree, int devno)
 {
@@ -405,6 +433,8 @@
 			}
 		}
 	}
+	if(some)
+		setconfstr();
 	wunlock(&lck);
 	if(some == 0 && alltrees == 0)
 		error(Enonexist);
@@ -560,9 +590,13 @@
 	Tree	*t;
 
 	/* ignore comments & empty lines */
-	if (*a == '\0' || *a == '#' || *a == '\n')
+	if (n < 1 || *a == '\0' || *a == '#' || *a == '\n')
 		return;
 
+	/* ignore historical config signature */
+	if (n >= 6 && memcmp(a, "fsdev:", 6) == 0)
+		return;
+
 	dprint("mconfig\n");
 	size = 0;
 	start = 0;
@@ -722,8 +756,9 @@
 	}
 	setdsize(mp, ilen);
 
-	poperror();
+	setconfstr();
 	wunlock(&lck);
+	poperror();
 	free(idev);
 	free(ilen);
 	free(cb);
@@ -732,22 +767,31 @@
 static void
 rdconf(void)
 {
-	int mustrd;
 	char *c, *e, *p, *s;
 	Chan *cc;
-	static int configed;
+	int mustrd;
 
 	/* only read config file once */
-	if (configed)
+	if (confstr != nil)
 		return;
-	configed = 1;
 
-	dprint("rdconf\n");
+	wlock(&lck);
+	if (confstr != nil) {
+		wunlock(&lck);
+		return;	/* already done */
+	}
+
 	/* add the std "fs" tree */
-	trees[0] = &fstree;
-	ntrees++;
-	fstree.name = "fs";
+	if(ntrees == 0){
+		fstree.name = "fs";
+		trees[ntrees++] = &fstree;
+	}
 
+	setconfstr();
+	wunlock(&lck);
+
+	dprint("rdconf\n");
+
 	/* identify the config file */
 	s = getconf("fsconfig");
 	if (s == nil){
@@ -756,7 +800,9 @@
 	} else
 		mustrd = 1;
 
+	c = smalloc(Maxconf+1);
 	if(waserror()){
+		free(c);
 		if(!mustrd)
 			return;
 		nexterror();
@@ -768,23 +814,12 @@
 		cclose(cc);
 		nexterror();
 	}
-	devtab[cc->type]->read(cc, confstr, sizeof confstr, 0);
-	poperror();
+	devtab[cc->type]->read(cc, c, Maxconf, 0);
 	cclose(cc);
+	poperror();
 
-	/* validate, copy and erase config; mconfig will repopulate confstr */
-	if (strncmp(confstr, cfgstr, sizeof cfgstr - 1) != 0)
-		error("bad #k config, first line must be: 'fsdev:\\n'");
-
-	c = nil;
-	kstrdup(&c, confstr + sizeof cfgstr - 1);
-	if(waserror()){
-		free(c);
-		nexterror();
-	}
-	memset(confstr, 0, sizeof confstr);
 	/* process config copy one line at a time */
-	for (p = c; p != nil && *p != '\0'; p = e){
+	for (p = c; *p != '\0'; p = e){
 		e = strchr(p, '\n');
 		if (e == nil)
 			e = p + strlen(p);
@@ -792,10 +827,9 @@
 			e++;
 		mconfig(p, e - p);
 	}
-	poperror();
-	free(c);
 
-	poperror();	/* mustrd */
+	free(c);
+	poperror();	/* c */
 }
 
 static int
@@ -1138,23 +1172,6 @@
 	return res;
 }
 
-static char*
-seprintconf(char *s, char *e)
-{
-	int	i, j;
-	Tree	*t;
-
-	*s = 0;
-	for(i = 0; i < ntrees; i++){
-		t = trees[i];
-		if(t != nil)
-			for(j = 0; j < t->nadevs; j++)
-				if(t->devs[j] != nil)
-					s = seprintdev(s, e, t->devs[j]);
-	}
-	return s;
-}
-
 static long
 mread(Chan *c, void *a, long n, vlong off)
 {
@@ -1175,7 +1192,6 @@
 		goto Done;
 	}
 	if(c->qid.path == Qctl){
-		seprintconf(confstr, confstr + sizeof(confstr));
 		res = readstr((long)off, a, n, confstr);
 		goto Done;
 	}