Magic symlinks redux

Luigi Rizzo rizzo at iet.unipi.it
Fri Aug 22 14:53:42 UTC 2008


On Fri, Aug 22, 2008 at 02:05:26PM +0200, Christian Brueffer wrote:
> On Fri, Aug 22, 2008 at 12:24:41PM +0200, Ivan Voras wrote:
...
> > This patch is huge. As far as I can tell DragonflyBSD has a whole 
> > framework dedicated to varsyms, spread across a fair part of the kernel 
> > and with at least one special userland utility. It allows the operator 
> > to define his own variables that can be used in the substitutions, and I 
> > don't see that it predefines "special" variables like "uid" and 
> > "hostname". It's not necessarily a bad solution but I consider it overkill.
...
> Brooks has a varsym port in p4, see //depot/user/brooks/varsym/

this also seems to be based on Dragonfly's code, quite intrusive.

I am playing with a rewrite (attached below) of the original patch,
which fixes at least one memory leak and addresses some of the
issues that i mentioned in this thread (abuse of macros, performance,
behaviour on errors, etc.).

(i haven't looked up yet the original copyright but i guess it
is from netbsd...)

	cheers
	luigi

Index: src/sys/kern/vfs_lookup.c
===================================================================
--- src/sys/kern/vfs_lookup.c	(revision 181995)
+++ src/sys/kern/vfs_lookup.c	(working copy)
@@ -46,6 +46,7 @@
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
+#include <sys/jail.h>	// XXX symlinks
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/mount.h>
@@ -88,6 +89,123 @@
 }
 SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nameiinit, NULL);
 
+#ifdef MAGICLINKS
+static int vfs_magiclinks = 1;
+#else
+static int vfs_magiclinks = 1;
+#endif
+SYSCTL_INT(_vfs, OID_AUTO, magiclinks, CTLFLAG_RW, &vfs_magiclinks, 0,
+	"Whether \"magic\" symlinks are expanded");
+
+/* looks up a string returns the match len or 0 */
+static int
+s_match(const char *key, int keylen, const char *haystack, const char *end)
+{
+	if (haystack + keylen >= end || haystack[keylen] != '}')
+		return 0;
+	if (strncmp(key, haystack, keylen))
+		return 0;
+	return keylen;
+}
+#define	MATCH(str) s_match(str, sizeof(str) - 1, src, end)
+
+static char *
+s_subst(char *dst, const char *max, const char *value, int len)
+{
+	if (value == dst) {	/* already copied, locate end of string */
+		while (*dst)
+			dst++;
+		return dst;
+	}
+	/* check size, copy and replace */
+	if (dst + len > max) /* overflow */
+		return NULL;
+	bcopy(value, dst, len);
+	dst += len;
+	return dst;
+}
+
+/*
+ * Substitute replacement text for 'magic' strings in symlinks.
+ * Looks for "@{string}", where <string> is a
+ * recognized 'magic' string.  Replaces the original with the
+ * appropriate replacement text.  (Note that in some cases the
+ * replacement text may have zero length.)
+ * Assume *len is at least 3.
+ */
+static void
+symlink_magic(struct thread *td, char *cp, int *len)
+{
+	char *src, *dst, *tmp, *end = cp + *len, *max;
+	int change = 0;
+
+	/* quick return if nothing to replace */
+	for (src = cp; src < end - 1; src++) {
+		if (src[0] == '@' && src[1] == '{')
+			break;
+	}
+	if (src == end - 1)	/* no replacement */
+		return;
+
+	/* allocate a buffer for the replacement */
+	dst = tmp = uma_zalloc(namei_zone, M_WAITOK);
+	if (dst == NULL) {	/* no space for replacement */
+		printf("zalloc fail in %s\n", __FUNCTION__);
+		return;
+	}
+	max = dst + MAXPATHLEN - 1;
+	for (src = cp; src < end - 1 && dst < max - 1;) {
+		int l;
+		if (src[0] != '@' || src[1] != '{') {
+			*dst++ = *src++;	/* copy current char */
+			continue;
+		}
+		src += 2;	/* skip @{ */
+
+printf("replace magic at %s\n", src);
+		/*
+		 * The following checks should be ordered according
+		 * to frequency of use.
+		 */
+		if ( (l = MATCH("machine_arch")) ) {
+			dst = s_subst(dst, max, MACHINE_ARCH, sizeof(MACHINE_ARCH) - 1);
+		} else if ( (l= MATCH("machine")) ) {
+			dst = s_subst(dst, max, MACHINE_ARCH, sizeof(MACHINE_ARCH) - 1);
+		} else if ( (l= MATCH("hostname")) ) {
+			getcredhostname(td->td_ucred, dst, max - dst);
+			dst = s_subst(dst, max, dst, 0);
+		} else if ( (l= MATCH("osrelease")) ) {
+			dst = s_subst(dst, max, osrelease, strlen(osrelease));
+		} else if ( (l= MATCH("kernel_ident")) ) {
+			dst = s_subst(dst, max, kern_ident, strlen(kern_ident));
+		} else if ( (l= MATCH("domainname")) ) {
+			dst = s_subst(dst, max, domainname, strlen(domainname));
+		} else if ( (l= MATCH("ostype")) ) {
+			dst = s_subst(dst, max, ostype, strlen(ostype));
+		}
+		if (dst == NULL)	/* overflow */
+			break;
+		if (l == 0) { /* no match, restore original */
+			*dst++ = '@';
+			*dst++ = '{';
+			continue;
+		}
+		/* otherwise skip original name and } */
+		src += l + 1;
+		change = 1;
+	}
+	if (change && dst) {
+		if (src < end)	/* copy last char */
+			*dst++ = *src;
+		*dst = '\0';
+		printf("translating into %s\n", tmp);
+		*len = dst - tmp;
+		bcopy(tmp, cp, *len);
+	}
+	uma_zfree(namei_zone, tmp);
+}
+#undef MATCH
+
 #ifdef LOOKUP_SHARED
 static int lookup_shared = 1;
 #else
@@ -284,6 +402,8 @@
 			error = ENOENT;
 			break;
 		}
+		if (vfs_magiclinks && linklen >3) /* at least @{} in the symlink */
+			symlink_magic(td, cp, &linklen);
 		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
 			if (ndp->ni_pathlen > 1)
 				uma_zfree(namei_zone, cp);


More information about the freebsd-arch mailing list