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