Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 22 Aug 2008 13:41:48 +0400
From:      "Andrey V. Elsukov" <bu7cher@yandex.ru>
To:        Luigi Rizzo <rizzo@iet.unipi.it>
Cc:        David Quattlebaum <David.Quattlebaum@sas.com>, Ivan Voras <ivoras@freebsd.org>, freebsd-arch@freebsd.org
Subject:   Re: Magic symlinks redux
Message-ID:  <48AE89DC.9080408@yandex.ru>
In-Reply-To: <20080822090448.GB57441@onelab2.iet.unipi.it>
References:  <g8kv7v$sp2$1@ger.gmane.org> <20080822090448.GB57441@onelab2.iet.unipi.it>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Luigi Rizzo wrote:
> interestingly simple.
> 
> Question - is the process' ENV easily available in this part
> of the kernel, so one could in principle use environment variables
> as replacement strings ?
> 
> Some comments on the code in the above patch:
> 
> + readability it might be improved a bit:
>   e.g. I don't see why uma_{zalloc|zfree} are hidden behind macros,
>   nor why symlynk_magic() isn't simply called as
> 
> 	if (vfs_magiclinks)
> 		symlink_magic(td, cp, &linklen);
> 
>   as it cannot fail as implemented;
>   also, the whole MATCH/SUBSTITUTE macros are not particularly
>   readable -- i understand one needs macros to implement sizeof("somestring")
>   correctly, but apart from a wrapper I believe the core of these two
>   blocks should be implemented by functions (possibly inline) with
>   MATCH() returning the match length so one doesn't need to replicate
>   the string parameter in SUBSTITUTE();
> 
> + correctness --
>   1. termchar is not reset to '/' if a match is not found
>   2. what is the intended behaviour when the replacement string overflows
>      the buffer ?
> 
> + efficiency of symlink_magic() might be improved too:
>   e.g. the function could do a quick check for the presence of @ and return
>   without allocation/deallocation if not found;
>   getcredhostname() (and similar routines) could be called so that
>   they write directly to tmp, without the need for
>   allocating an in-stack buffer

This was so long ago.. As i remember this patch is a quick port of
NetBSD's implementation and uses the same code.

Also there was another implementation ported from DragonFlyBSD.
David Quattlebaum is working on varsyms implementation and he sent
fresh patch to me in this April. I attached patch.
And sorry, i am not working on this today..

-- 
WBR, Andrey V. Elsukov

[-- Attachment #2 --]
diff -U3 -N src/sys/kern/kern_varsym.c src/sys/kern/kern_varsym.c
--- src/sys/kern/kern_varsym.c	Wed Dec 31 19:00:00 1969
+++ src/sys/kern/kern_varsym.c	Thu Feb 16 09:31:08 2006
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $FreeBSD$
+ * $DragonFly: src/sys/kern/kern_varsym.c,v 1.6 2005/01/14 02:25:08 joerg Exp $
+ */
+
+/*
+ * This module implements variable storage and management for variant
+ * symlinks.  These variables may also be used for general purposes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ucred.h>
+#include <sys/resourcevar.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/jail.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/varsym.h>
+#include <sys/sysproto.h>
+
+MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
+
+struct varsymset	varsymset_sys;
+
+static int		varsymmake(int level, const char *name, const char *data);
+static void		varsymdrop(varsym_t var);
+static struct varsyment *
+			varsymlookup(struct varsymset *vss, const char *name, int namelen);
+static varsym_t		varsymfind(int mask, const char *name, int namelen);
+
+int varsym_col1only = 0;
+SYSCTL_INT(_vfs, OID_AUTO, varsym_col1only, CTLFLAG_RW, &varsym_col1only, 0,
+    "Varsym variables start in column 1 only");
+
+/*
+ * Initialize the variant symlink subsystem
+ */
+static void
+varsym_sysinit(void *dummy)
+{
+    varsymset_init(&varsymset_sys, NULL);
+}
+SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL);
+
+/*
+ * Initialize the varsymset for proc0
+ */
+static void
+varsym_p0init(void *dummy)
+{
+	varsymset_init(&proc0.p_varsymset, NULL);
+}
+SYSINIT(p0init, SI_SUB_INTRINSIC, SI_ORDER_SECOND, varsym_p0init, NULL);
+
+/*
+ * varsymreplace() - called from namei
+ *
+ *	Do variant symlink variable substitution
+ */
+int
+varsymreplace(char *cp, int linklen, int maxlen)
+{
+    int rlen;
+    int xlen;
+    int nlen;
+    int i;
+    varsym_t var;
+
+    rlen = linklen;
+    while (linklen > 1) {
+	if (cp[0] == '$' && cp[1] == '{') {
+		for (i = 2; i < linklen; ++i) {
+			if (cp[i] == '}')
+		    	break;
+		}
+		/*
+		 * In jail we should reset VARSYM_USER_MASK, we check it
+		 * into varsymfind.
+		 */
+	    if (i < linklen && (
+			var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL) 
+		{
+			xlen = i + 1;			/* bytes to strike */
+			nlen = strlen(var->vs_data);	/* bytes to add */
+			if (linklen + nlen - xlen >= maxlen) 
+			{
+		    	varsymdrop(var);
+		    	return(-1);
+			}
+			KASSERT(linklen >= xlen, ("%s: linklen < xlen",__func__));
+			if (linklen != xlen)
+				bcopy(cp + xlen, cp + nlen, linklen - xlen);
+			bcopy(var->vs_data, cp, nlen);
+			linklen += nlen - xlen;	/* new relative length */
+			rlen += nlen - xlen;	/* returned total length */
+			cp += nlen;		/* adjust past replacement */
+			linklen -= nlen;	/* adjust past replacement */
+			maxlen -= nlen;		/* adjust past replacement */
+	    } else {
+			/*
+			 * It's ok if i points to the '}', it will simply be
+			 * skipped.  i could also have hit linklen.
+			 */
+			cp += i;
+			linklen -= i;
+			maxlen -= i;
+	    }
+	} else {
+	    /*
+	     * Break if we didn't hit a $ in col. 1 if varsym_col1only != 0
+	     */
+	    if (varsym_col1only != 0)
+		break;
+	    ++cp;
+	    --linklen;
+	    --maxlen;
+	}
+    }
+    return(rlen);
+}
+
+/*
+ * varsym_set() system call
+ *
+ * (int level, const char *name, const char *data)
+ */
+int
+varsym_set(struct thread *td, struct varsym_set_args *uap)
+{
+    char name[MAXVARSYM_NAME];
+    char *buf;
+    int error;
+
+    if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
+		goto done2;
+    
+	buf = malloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK);
+    if (uap->data && 
+	(error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
+    {
+		goto done1;
+    }
+    
+	switch(uap->level) {
+	case VARSYM_SYS:
+		if (td->td_proc != NULL) 
+		{
+			/* 
+		 	 * Only root can change SYSTEM's varsyms. 
+			 * Prison's root can change only SYSTEM's varsyms
+			 * for own prison.
+			 */
+			error = priv_check_cred(td->td_proc->p_ucred, PRIV_CRED_SETUID, 0);
+			if (error != 0)
+				break; /* error = EPERM */
+			if (jailed(td->td_proc->p_ucred))
+				uap->level = VARSYM_PRISON;
+		} 
+		/* fall through */
+	case VARSYM_USER:
+		/* XXX implement per-jail user */
+		if (td->td_proc != NULL && 
+			uap->level == VARSYM_USER &&
+			jailed(td->td_proc->p_ucred)) 
+		{
+			/* 
+			 * Deny change users varsyms for jailed proceses. 
+			 * (we don't have a per-jaileduser uidinfo)
+			 */
+			error = EPERM;
+			break;
+		}
+		/* fall through */
+	case VARSYM_PPROC:
+	case VARSYM_PROC:
+		error = varsymmake(uap->level, name, NULL);
+		if (uap->data) 
+			error = varsymmake(uap->level, name, buf);
+		break;
+	default:
+		error = EINVAL;
+    }
+done1:
+	free(buf, M_TEMP);
+done2:
+    return(error);
+}
+
+/*
+ * varsym_get() system call
+ *
+ * (int mask, const char *wild, char *buf, int *size)
+ */
+int
+varsym_get(struct thread *td, struct varsym_get_args *uap)
+{
+    char wild[MAXVARSYM_NAME];
+    varsym_t sym;
+    int error;
+    int dlen, bufsize;
+
+    if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
+		goto done;
+    if ((error = copyin(uap->size, &bufsize, sizeof(bufsize))) != 0)
+		goto done;
+	if (uap->mask & (~VARSYM_ALL_MASK)) {
+		error = EINVAL;
+		goto done;
+	}
+    sym = varsymfind(uap->mask, wild, strlen(wild));
+    if (sym == NULL) {
+		error = ENOENT;
+		goto done;
+    }
+    dlen = strlen(sym->vs_data);
+    if (dlen < bufsize) {
+		error = copyout(sym->vs_data, uap->buf, dlen + 1);
+    } else 
+		error = EOVERFLOW; /* buffer too small */
+	if (error == 0) {
+	    dlen++;
+		error = copyout(&dlen, uap->size, sizeof(dlen));
+	}
+    varsymdrop(sym);
+done:
+    return(error);
+}
+
+/*
+ * varsym_list() system call
+ *
+ * (int level, char *buf, int *size, int *marker)
+ */
+int
+varsym_list(struct thread *td, struct varsym_list_args *uap)
+{
+	struct varsymset *vss;
+	struct varsyment *ve;
+	struct proc *p;
+	int i;
+	int error;
+	int bytes;
+	int earlyterm;
+	int marker;
+	int maxsize;
+
+	/*
+	 * Get the marker and maxsize from userspace.
+	 */
+	if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0)
+		goto done;
+	if ((error = copyin(uap->size, &maxsize, sizeof(maxsize))) != 0)
+		goto done;
+	
+	/*
+	 * Figure out the varsym set.
+	 */
+	p = td->td_proc;
+	vss = NULL;
+
+	switch (uap->level) {
+	case VARSYM_PPROC:
+		if (p && p->p_pptr)
+			vss = &p->p_pptr->p_varsymset;
+		break;
+	case VARSYM_PROC:
+		if (p)
+			vss = &p->p_varsymset;
+		break;
+	case VARSYM_USER:
+		if (p) {
+			/*
+			 * XXX: Jailed users currently not supported.
+			 */
+			if (jailed(p->p_ucred)) {
+				error = EPERM;
+				goto done;
+			}
+			vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
+		}
+		break;
+	case VARSYM_SYS:
+		if (p != NULL && jailed(p->p_ucred))
+			vss = &p->p_ucred->cr_prison->pr_varsymset;
+		else
+			vss = &varsymset_sys;
+	}
+	if (vss == NULL) {
+		error = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Loop through the variables and dump them to uap->buf
+	 */
+	i = 0;
+	bytes = 0;
+	earlyterm = 0;
+
+	TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
+		varsym_t sym = ve->ve_sym;
+		int namelen = strlen(sym->vs_name);
+		int datalen = strlen(sym->vs_data);
+		int totlen = namelen + datalen + 2;
+
+		/*
+		 * Skip to our index point
+		 */
+		if (i < marker) {
+			++i;
+			continue;
+		}
+
+		/*
+		 * Stop if there is insufficient space in the user buffer.
+		 * If we haven't stored anything yet return EOVERFLOW. 
+		 * Note that the marker index (i) does not change.
+		 */
+		if (bytes + totlen > maxsize) {
+			if (bytes == 0)
+				error = EOVERFLOW;
+			earlyterm = 1;
+			break;
+		}
+
+		error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1);
+		if (error == 0) {
+			bytes += namelen + 1;
+			error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1);
+			if (error == 0)
+				bytes += datalen + 1;
+			else
+				bytes -= namelen + 1;	/* revert if error */
+		}
+		if (error) {
+			earlyterm = 1;
+			break;
+		}
+		++i;
+	}
+
+	/*
+	 * Save the marker back.  If no error occured and earlyterm is clear
+	 * the marker is set to -1 indicating that the variable list has been
+	 * exhausted.  If no error occured the number of bytes loaded into
+	 * the buffer will be returned, otherwise the syscall code returns -1.
+	 */
+	if (error == 0 && earlyterm == 0)
+		marker = -1;
+	else
+		marker = i;
+	if (error == 0)
+		error = copyout(&marker, uap->marker, sizeof(marker));
+	if (error == 0)
+		error = copyout(&bytes, uap->size, sizeof(bytes));
+done:
+	return(error);
+}
+
+/*
+ * Lookup a variant symlink.  XXX use a hash table.
+ */
+static
+struct varsyment *
+varsymlookup(struct varsymset *vss, const char *name, int namelen)
+{
+    struct varsyment *ve;
+
+    TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
+		varsym_t var = ve->ve_sym;
+		if (var->vs_namelen == namelen && 
+	    	bcmp(name, var->vs_name, namelen) == 0) 
+		{
+	    	return(ve);
+		}
+    }
+    return(NULL);
+}
+
+static varsym_t
+varsymfind(int mask, const char *name, int namelen)
+{
+    struct proc *p = curproc;
+    struct varsyment *ve = NULL;
+    varsym_t sym;
+
+	/*
+	 * per-process variables override per-user variables,
+         * per-process variables override parent process variables,
+	 * per-user variables override system-wide variables.
+	 * XXX: per-user variables currently not allowed in jail.
+	 */
+    if ((mask & (VARSYM_PROC_MASK|VARSYM_PPROC_MASK|VARSYM_USER_MASK)) && p != NULL) {
+		if (mask & VARSYM_PROC_MASK)
+			ve = varsymlookup(&p->p_varsymset, name, namelen);
+		if (ve == NULL && mask & VARSYM_PPROC_MASK)
+			ve = varsymlookup(&p->p_pptr->p_varsymset, name, namelen);
+		if (ve == NULL && (mask & VARSYM_USER_MASK) && (!jailed(p->p_ucred)))
+	   		ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, 
+							name, namelen);
+    }
+    if (ve == NULL && (mask & VARSYM_SYS_MASK)) {
+		if (p != NULL && jailed(p->p_ucred)) 
+	    	ve = varsymlookup(&p->p_ucred->cr_prison->pr_varsymset, 
+							name, namelen);
+		else
+	    	ve = varsymlookup(&varsymset_sys, name, namelen);
+    }
+    if (ve) {
+		sym = ve->ve_sym;
+		++sym->vs_refs;
+		return(sym);
+    } 
+	return(NULL);
+}
+
+static int
+varsymmake(int level, const char *name, const char *data)
+{
+    varsym_t sym;
+    struct varsymset *vss = NULL;
+    struct varsyment *ve;
+    struct proc *p = curproc;
+    int namelen = strlen(name);
+    int datalen;
+    int error;
+
+	switch(level) {
+	case VARSYM_PPROC:
+		if (p && p->p_pptr)
+		vss = &p->p_pptr->p_varsymset;
+		break;
+	case VARSYM_PROC:
+		if (p)
+	 	vss = &p->p_varsymset;
+		break;
+	case VARSYM_USER:
+		if (p)
+	    	vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
+		break;
+	case VARSYM_SYS:
+		vss = &varsymset_sys;
+		break;
+	case VARSYM_PRISON:
+		if (p)
+	    	vss = &p->p_ucred->cr_prison->pr_varsymset;
+		break;
+    }
+    if (vss == NULL) {
+		error = EINVAL;
+    } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
+		error = E2BIG;
+    } else if (data) {
+		datalen = strlen(data);
+		ve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
+		sym = malloc(sizeof(struct varsym) + namelen + datalen + 2,
+					   	M_VARSYM, M_WAITOK);
+		ve->ve_sym = sym;
+		sym->vs_refs = 1;
+		sym->vs_namelen = namelen;
+		sym->vs_name = (char *)(sym + 1);
+		sym->vs_data = sym->vs_name + namelen + 1;
+		strcpy(sym->vs_name, name);
+		strcpy(sym->vs_data, data);
+		
+		TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
+		vss->vx_setsize += sizeof(struct varsyment) + 
+				sizeof(struct varsym) + namelen + datalen + 8; /* XXX */
+
+		error = 0;
+    } else {
+		if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
+	    	TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
+		    vss->vx_setsize -= sizeof(struct varsyment) + 
+							   sizeof(struct varsym) + namelen + 
+							   strlen(ve->ve_sym->vs_data) + 8; /* XXX */
+		    varsymdrop(ve->ve_sym);
+		    free(ve, M_VARSYM);
+	    	error = 0;
+		} else {
+	    	error = ENOENT;
+		}
+    }
+    return(error);
+}
+
+static void
+varsymdrop(varsym_t sym)
+{
+    KASSERT(sym->vs_refs > 0, ("%s: sym->vs_refs <= 0", __func__));
+    if (--sym->vs_refs == 0) {
+		free(sym, M_VARSYM);
+    }
+}
+
+void
+varsymset_init(struct varsymset *vss, struct varsymset *copy)
+{
+    struct varsyment *ve, *nve;
+
+    TAILQ_INIT(&vss->vx_queue);
+	
+    if (copy) {
+		TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
+			nve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
+			nve->ve_sym = ve->ve_sym;
+			++nve->ve_sym->vs_refs;
+			TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
+		}
+		vss->vx_setsize = copy->vx_setsize;
+    }
+}
+
+void
+varsymset_clean(struct varsymset *vss)
+{
+    struct varsyment *ve;
+
+    while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
+		TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
+		varsymdrop(ve->ve_sym);
+		free(ve, M_VARSYM);
+    }
+    vss->vx_setsize = 0;
+}
+
Index: src/bin/ln/ln.1
===================================================================
RCS file: /ncvs/src/bin/ln/ln.1,v
retrieving revision 1.30
diff -u -b -r1.30 ln.1
--- src/bin/ln/ln.1	16 Jan 2005 16:41:57 -0000	1.30
+++ src/bin/ln/ln.1	22 Jan 2006 22:59:39 -0000
@@ -186,6 +186,47 @@
 which performs a
 .Xr link 2
 operation using the two passed arguments.
+.Sh VARIANT SYMLINKS
+.Dx
+supports a special kind of dynamic
+symbolic link called a
+.Em variant symlink .
+The
+.Ar source_file
+of a variant symlink may contain one or more
+variable names.  Each of these variable
+names is enclosed in braces and preceded by a
+dollar sign in the style of variable references in
+.Xr sh 1
+and
+.Xr csh 1 .
+.Pp
+Whenever a variant symlink is followed, each
+variable found in
+.Ar source_file
+is replaced by its associated value.
+In this manner, a variant symlink may resolve to different
+paths based on context.  The facility
+supports per-process, per-user, and system-wide varsyms.
+.Pp
+Varsym variables can be set with the
+.Xr varsym 1
+utility.  Regular
+.Xr environ 7
+environment variables are
+not used to resolve variant symlinks.
+.Ss EXAMPLE
+.Bd -literal -offset indent
+sysctl -w vfs.varsym_enable=1
+
+ln -s 'a${fubar}b' test
+
+echo 'Hello' > axxb
+echo 'Goodbye' > ayyb
+
+varsym fubar=xx; cat test
+varsym fubar=yy; cat test
+.Ed
 .Sh COMPATIBILITY
 The
 .Fl h ,
@@ -209,7 +250,8 @@
 .Xr readlink 2 ,
 .Xr stat 2 ,
 .Xr symlink 2 ,
-.Xr symlink 7
+.Xr symlink 7 ,
+.Xr varsym 1
 .Sh STANDARDS
 The
 .Nm
@@ -224,4 +266,4 @@
 An
 .Nm
 command appeared in
-.At v1 .
+.At v1 . Variant Symlinks appeared in DragonFly BSD .
Index: src/include/unistd.h
===================================================================
RCS file: /ncvs/src/include/unistd.h,v
retrieving revision 1.78
diff -u -b -r1.78 unistd.h
--- src/include/unistd.h	13 May 2005 16:27:30 -0000	1.78
+++ src/include/unistd.h	22 Jan 2006 22:59:39 -0000
@@ -550,6 +550,10 @@
 int	 unwhiteout(const char *);
 void	*valloc(size_t);			/* obsoleted by malloc() */
 
+int	 varsym_set(int, const char*, const char*);
+int	 varsym_get(int, const char*, char*, int*);
+int	 varsym_list(int, char*, int*, int*);
+
 #ifndef _OPTRESET_DECLARED
 #define	_OPTRESET_DECLARED
 extern int optreset;			/* getopt(3) external variable */
Index: src/sys/conf/files
===================================================================
RCS file: /ncvs/src/sys/conf/files,v
retrieving revision 1.1031.2.16
diff -u -b -r1.1031.2.16 files
--- src/sys/conf/files	13 Jan 2006 14:55:16 -0000	1.1031.2.16
+++ src/sys/conf/files	22 Jan 2006 22:59:39 -0000
@@ -1243,6 +1243,7 @@
 kern/kern_timeout.c		standard
 kern/kern_umtx.c		standard
 kern/kern_uuid.c		standard
+kern/kern_varsym.c		standard
 kern/kern_xxx.c			standard
 kern/link_elf.c			standard
 kern/linker_if.m		standard
Index: src/sys/compat/freebsd32/syscalls.master
===================================================================
RCS file: /ncvs/src/sys/compat/freebsd32/syscalls.master,v
retrieving revision 1.50.2.3
diff -u -b -r1.50.2.3 syscalls.master
--- src/sys/compat/freebsd32/syscalls.master.orig	Sat Oct 28 19:49:05 2006
+++ src/sys/compat/freebsd32/syscalls.master	Mon Aug 27 21:04:19 2007
@@ -796,3 +796,11 @@
 480	AUE_FTRUNCATE	STD	{ int freebsd32_ftruncate(int fd, \
 				    u_int32_t lengthlo, u_int32_t lengthhi); }
 481	AUE_KILL	NOPROTO	{ int thr_kill2(pid_t pid, long id, int sig); }
+482	AUE_NULL	NOPROTO	{ int varsym_set(int level, \
+				    const char *name, \
+				    const char *data); }
+483	AUE_NULL	NOPROTO	{ int varsym_get(int mask, \
+				    const char *wild, \
+				    char *buf, int *size); }
+484	AUE_NULL	NOPROTO	{ int varsym_list(int level, \
+				    char *buf, int *size, int *marker); }
Index: src/sys/kern/syscalls.master
===================================================================
RCS file: /ncvs/src/sys/kern/syscalls.master,v
retrieving revision 1.198.2.1
diff -u -b -r1.198.2.1 syscalls.master
--- src/sys/kern/syscalls.master	21 Nov 2005 01:12:37 -0000	1.198.2.1
+++ src/sys/kern/syscalls.master	22 Jan 2006 22:59:40 -0000
@@ -847,5 +847,13 @@
 479	AUE_TRUNCATE	STD	{ int truncate(char *path, off_t length); }
 480	AUE_FTRUNCATE	STD	{ int ftruncate(int fd, off_t length); }
 481	AUE_KILL	STD	{ int thr_kill2(pid_t pid, long id, int sig); }
+482     AUE_NULL        STD	{ int varsym_set(int level, \
+				     const char *name, \
+				     const char *data); }
+483     AUE_NULL        STD	{ int varsym_get(int mask, \
+				     const char *wild, \
+				     char *buf, int *size); }
+484     AUE_NULL        STD	{ int varsym_list(int level, \
+				     char *buf, int *size, int *marker); }
 ; Please copy any additions and changes to the following compatability tables:
 ; sys/compat/freebsd32/syscalls.master
Index: src/sys/kern/kern_exec.c
===================================================================
RCS file: /ncvs/src/sys/kern/kern_exec.c,v
retrieving revision 1.275.2.3
diff -u -b -r1.275.2.3 kern_exec.c
--- src/sys/kern/kern_exec.c	26 Dec 2005 13:47:19 -0000	1.275.2.3
+++ src/sys/kern/kern_exec.c	22 Jan 2006 22:59:40 -0000
@@ -617,6 +617,11 @@
 		change_svgid(newcred, newcred->cr_gid);
 		p->p_ucred = newcred;
 		newcred = NULL;
+
+		/*
+		 * Clear local varsym variables
+		 */
+		varsymset_clean(&p->p_varsymset);
 	} else {
 		if (oldcred->cr_uid == oldcred->cr_ruid &&
 		    oldcred->cr_gid == oldcred->cr_rgid)
Index: src/sys/kern/kern_fork.c
===================================================================
RCS file: /ncvs/src/sys/kern/kern_fork.c,v
retrieving revision 1.252
diff -u -b -r1.252 kern_fork.c
--- src/sys/kern/kern_fork.c	1 Jul 2005 16:28:30 -0000	1.252
+++ src/sys/kern/kern_fork.c	22 Jan 2006 22:59:41 -0000
@@ -596,7 +596,7 @@
 	LIST_INSERT_AFTER(p1, p2, p_pglist);
 	PGRP_UNLOCK(p1->p_pgrp);
 	LIST_INIT(&p2->p_children);
-
+	varsymset_init(&p2->p_varsymset, &p1->p_varsymset);
 	callout_init(&p2->p_itcallout, CALLOUT_MPSAFE);
 
 #ifdef KTRACE
Index: src/sys/kern/kern_jail.c
===================================================================
RCS file: /ncvs/src/sys/kern/kern_jail.c,v
retrieving revision 1.50.2.1
diff -u -b -r1.50.2.1 kern_jail.c
--- src/sys/kern/kern_jail.c	13 Nov 2005 03:12:32 -0000	1.50.2.1
+++ src/sys/kern/kern_jail.c	22 Jan 2006 22:59:41 -0000
@@ -163,6 +163,7 @@
 		pr->pr_slots = malloc(sizeof(*pr->pr_slots) * prison_service_slots,
 		    M_PRISON, M_ZERO | M_WAITOK);
 	}
+	varsymset_init(&pr->pr_varsymset, NULL);
 
 	/* Determine next pr_id and add prison to allprison list. */
 	sx_xlock(&allprison_lock);
@@ -176,7 +177,7 @@
 			if (tryprid == JAIL_MAX) {
 				sx_xunlock(&allprison_lock);
 				error = EAGAIN;
-				goto e_dropvnref;
+				goto e_cleanvarsym;
 			}
 			goto next;
 		}
@@ -207,6 +208,8 @@
 		psrv->ps_destroy(psrv, pr);
 	}
 	sx_sunlock(&allprison_lock);
+e_cleanvarsym:
+	varsymset_clean(&pr->pr_varsymset);
 e_dropvnref:
 	vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount);
 	vrele(pr->pr_root);
@@ -343,6 +346,7 @@
 	vrele(pr->pr_root);
 	VFS_UNLOCK_GIANT(vfslocked);
 
+	varsymset_clean(&pr->pr_varsymset);
 	mtx_destroy(&pr->pr_mtx);
 	if (pr->pr_linux != NULL)
 		FREE(pr->pr_linux, M_PRISON);
Index: src/sys/kern/kern_resource.c
===================================================================
RCS file: /ncvs/src/sys/kern/kern_resource.c,v
retrieving revision 1.148.2.1
diff -u -b -r1.148.2.1 kern_resource.c
--- src/sys/kern/kern_resource.c	28 Dec 2005 17:35:55 -0000	1.148.2.1
+++ src/sys/kern/kern_resource.c	22 Jan 2006 22:59:41 -0000
@@ -1055,6 +1055,7 @@
 		} else {
 			uip->ui_mtxp = mtx_pool_alloc(mtxpool_sleep);
 			uip->ui_uid = uid;
+			varsymset_init(&uip->ui_varsymset, NULL);
 			LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash);
 		}
 	}
@@ -1125,6 +1126,7 @@
 		if (uip->ui_proccnt != 0)
 			printf("freeing uidinfo: uid = %d, proccnt = %ld\n",
 			    uip->ui_uid, uip->ui_proccnt);
+		varsymset_clean(&uip->ui_varsymset);
 		UIDINFO_UNLOCK(uip);
 		FREE(uip, M_UIDINFO);
 		return;
Index: src/sys/kern/vfs_lookup.c
===================================================================
RCS file: /ncvs/src/sys/kern/vfs_lookup.c,v
retrieving revision 1.80.2.3
diff -u -b -r1.80.2.3 vfs_lookup.c
--- src/sys/kern/vfs_lookup.c	29 Sep 2005 18:53:10 -0000	1.80.2.3
+++ src/sys/kern/vfs_lookup.c	22 Jan 2006 22:59:41 -0000
@@ -63,6 +63,10 @@
 #define	NAMEI_DIAGNOSTIC 1
 #undef NAMEI_DIAGNOSTIC
 
+int varsym_enable = 0;
+SYSCTL_INT(_vfs, OID_AUTO, varsym_enable, CTLFLAG_RW, &varsym_enable, 0,
+    "Enable Variant Symlinks");
+
 /*
  * Allocation zone for namei
  */
@@ -262,6 +266,8 @@
 			break;
 		}
 		linklen = MAXPATHLEN - auio.uio_resid;
+		if (varsym_enable)
+			linklen = varsymreplace(cp, linklen, MAXPATHLEN);
 		if (linklen == 0) {
 			if (ndp->ni_pathlen > 1)
 				uma_zfree(namei_zone, cp);
Index: src/sys/sys/jail.h
===================================================================
RCS file: /ncvs/src/sys/sys/jail.h,v
retrieving revision 1.26
diff -u -b -r1.26 jail.h
--- src/sys/sys/jail.h	9 Jun 2005 18:49:19 -0000	1.26
+++ src/sys/sys/jail.h	22 Jan 2006 22:59:42 -0000
@@ -61,6 +61,7 @@
  *   (d) set only during destruction of jail, no mutex needed
  */
 #if defined(_KERNEL) || defined(_WANT_PRISON)
+#include <sys/varsym.h>
 struct prison {
 	LIST_ENTRY(prison) pr_list;			/* (a) all prisons */
 	int		 pr_id;				/* (c) prison id */
@@ -72,6 +73,7 @@
 	void		*pr_linux;			/* (p) linux abi */
 	int		 pr_securelevel;		/* (p) securelevel */
 	struct task	 pr_task;			/* (d) destroy task */
+	struct varsymset pr_varsymset;			/* (p) prison varsyms */
 	struct mtx	 pr_mtx;
 };
 #endif /* _KERNEL || _WANT_PRISON */
Index: src/sys/sys/namei.h
===================================================================
RCS file: /ncvs/src/sys/sys/namei.h,v
retrieving revision 1.47
diff -u -b -r1.47 namei.h
--- src/sys/sys/namei.h	27 Apr 2005 09:00:47 -0000	1.47
+++ src/sys/sys/namei.h	22 Jan 2006 22:59:42 -0000
@@ -174,6 +174,8 @@
 
 void NDFREE(struct nameidata *, const u_int);
 
+extern int varsym_enable;
+
 int	namei(struct nameidata *ndp);
 int	lookup(struct nameidata *ndp);
 int	relookup(struct vnode *dvp, struct vnode **vpp,
Index: src/sys/sys/proc.h
===================================================================
RCS file: /ncvs/src/sys/sys/proc.h,v
retrieving revision 1.432.2.2
diff -u -b -r1.432.2.2 proc.h
--- src/sys/sys/proc.h	4 Oct 2005 04:41:26 -0000	1.432.2.2
+++ src/sys/sys/proc.h	22 Jan 2006 22:59:42 -0000
@@ -60,6 +60,7 @@
 #endif
 #include <sys/ucontext.h>
 #include <sys/ucred.h>
+#include <sys/varsym.h>
 #include <machine/proc.h>		/* Machine-dependent proc substruct. */
 
 /*
@@ -512,6 +513,7 @@
 	LIST_ENTRY(proc) p_sibling;	/* (e) List of sibling processes. */
 	LIST_HEAD(, proc) p_children;	/* (e) Pointer to list of children. */
 	struct mtx	p_mtx;		/* (n) Lock for this struct. */
+	struct varsymset p_varsymset;	/* (c) process varsymset */
 	struct ksiginfo *p_ksi;	/* Locked by parent proc lock */
 	sigqueue_t	p_sigqueue;	/* (c) Sigs not delivered to a td. */
 #define p_siglist	p_sigqueue.sq_signals
Index: src/sys/sys/resourcevar.h
===================================================================
RCS file: /ncvs/src/sys/sys/resourcevar.h,v
retrieving revision 1.47
diff -u -b -r1.47 resourcevar.h
--- src/sys/sys/resourcevar.h	7 Jan 2005 02:29:23 -0000	1.47
+++ src/sys/sys/resourcevar.h	22 Jan 2006 22:59:42 -0000
@@ -38,6 +38,7 @@
 #ifdef _KERNEL
 #include <sys/_lock.h>
 #include <sys/_mutex.h>
+#include <sys/varsym.h>
 #endif
 
 /*
@@ -99,6 +100,7 @@
 	long	ui_proccnt;		/* (b) number of processes */
 	uid_t	ui_uid;			/* (a) uid */
 	u_int	ui_ref;			/* (b) reference count */
+	struct varsymset ui_varsymset;	/* (b) per-user varsyms */
 	struct mtx *ui_mtxp;		/* protect all counts/limits */
 };

Index: src/bin/Makefile
===================================================================
RCS file: /ncvs/src/bin/Makefile,v
retrieving revision 1.25
diff -u -b -r1.25 Makefile
--- src/bin/Makefile    2 Mar 2005 11:53:21 -0000       1.25
+++ src/bin/Makefile    26 Jan 2006 20:43:50 -0000
@@ -35,7 +35,8 @@
	sleep \
	stty \
	sync \
-	test
+	test \
+	varsym

 .if !defined(NO_RCMDS)
 _rcp=	rcp

diff -U3 -N src/sys/sys/varsym.h src/sys/sys/varsym.h
--- src/sys/sys/varsym.h	Wed Dec 31 19:00:00 1969
+++ src/sys/sys/varsym.h	Thu Feb 16 09:31:14 2006
@@ -0,0 +1,56 @@
+/*
+ * SYS/VARSYM.H
+ *
+ *	Implements structures used for variant symlink support.
+ * 
+ * $FreeBSD$
+ * $DragonFly: src/sys/sys/varsym.h,v 1.3 2005/01/14 02:25:08 joerg Exp $
+ */
+
+#ifndef _SYS_VARSYM_H_
+#define _SYS_VARSYM_H_
+#include <sys/queue.h>		/* TAILQ_* macros */
+
+struct varsym {
+    int		vs_refs;	/* a lot of sharing occurs */
+    int		vs_namelen;
+    char	*vs_name;	/* variable name */
+    char	*vs_data;	/* variable contents */
+};
+typedef struct varsym	*varsym_t;
+
+struct varsyment {
+    TAILQ_ENTRY(varsyment) ve_entry;
+    varsym_t	ve_sym;
+};
+
+struct varsymset {
+    TAILQ_HEAD(, varsyment) vx_queue;
+    int		vx_setsize;
+};
+
+#define VARSYM_PROC	1
+#define VARSYM_USER	2
+#define VARSYM_SYS	3
+#define VARSYM_PRISON	4	/* used internally */
+#define VARSYM_PPROC	5	/* set/get vars in parent process */
+
+#define VARSYM_PROC_MASK	(1 << VARSYM_PROC)
+#define VARSYM_USER_MASK	(1 << VARSYM_USER)
+#define VARSYM_SYS_MASK		(1 << VARSYM_SYS)
+#define VARSYM_PPROC_MASK	(1 << VARSYM_PPROC)
+#define VARSYM_ALL_MASK		(VARSYM_PROC_MASK|VARSYM_USER_MASK|VARSYM_SYS_MASK|VARSYM_PPROC_MASK)
+
+#define MAXVARSYM_NAME	64
+#define MAXVARSYM_DATA	256
+#define MAXVARSYM_SET	8192
+
+#ifdef _KERNEL
+
+void	varsymset_init(struct varsymset *varsymset, struct varsymset *copy);
+void	varsymset_clean(struct varsymset *varsymset);
+int	varsymreplace(char *cp, int linklen, int maxlen);
+
+#endif	/* _KERNEL */
+
+#endif
diff -U3 -N src/bin/varsym/Makefile src/bin/varsym/Makefile
--- src/bin/varsym/Makefile	Wed Dec 31 19:00:00 1969
+++ src/bin/varsym/Makefile	Fri Feb  3 13:05:44 2006
@@ -0,0 +1,6 @@
+# $FreeBSD$
+# $DragonFly: src/bin/varsym/Makefile,v 1.4 2005/02/06 06:16:40 okumoto Exp $
+
+PROG=	varsym
+
+.include <bsd.prog.mk>
diff -U3 -N src/bin/varsym/varsym.1 src/bin/varsym/varsym.1
--- src/bin/varsym/varsym.1	Wed Dec 31 19:00:00 1969
+++ src/bin/varsym/varsym.1	Fri Feb  3 13:13:10 2006
@@ -0,0 +1,130 @@
+.\" Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
+.\" All rights reserved.
+.\"
+.\" Modified by Andrey V. Elsukov <bu7cher@yandex.ru>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" $DragonFly: src/bin/varsym/varsym.1,v 1.4 2005/08/01 01:49:16 swildner Exp $
+.\"
+.Dd Nov 5, 2003
+.Dt VARSYM 1
+.Os
+.Sh NAME
+.Nm varsym
+.Nd get and set user and system-wide variables for variant symlinks
+.Sh SYNOPSIS
+.Nm
+.Op Fl AadPpqsv
+.Op Fl j Ar prison_id
+.Op Fl u Ar user_name | user_id
+.Ar var[=data]
+.Sh DESCRIPTION
+The
+.Nm
+program manages user and system-wide variables.  These variables are typically
+used by the system to resolve variant symlinks but may also be used generally.
+.Pp
+For each operand set, modify, retrieve, or delete the specified variable.
+By default variables specified without data are retrieved and variables
+specified with data are set.  Variables may be set to empty.
+.Bl -tag -width Ar
+.It Fl a
+List all variables at the specified level.  Note that per-user variables
+override system-wide variables.  By default, per-user variables are listed. 
+In the jail only system-wide variables are listed.
+.It Fl A
+List all variables at all levels in the order they would be found. The
+.Fl v
+option will show the level from which each variable came using the
+P, p, s or u prefix characters.
+.It Fl d
+Delete mode.  The specified variables are deleted.  Any specified data is
+ignored.
+.It Fl j Ar prison_id
+This option causes variables to be set systetm-wide and restricts retrievals
+to system-specific variables for jailed environment with id 
+.Cm prison_id Ns .
+Note that 
+.Fl j
+option is mutually exclusive with
+.Fl u
+option.
+.It Fl p
+This option causes variables to be set on a per-process basis and restricts
+retrievals to process-specific variables.  Note that since
+.Nm
+is run as its own process, using this option to set a variable will not
+affect your shell's namespace.
+.It Fl P
+This option causes variables to be set in the parent process (i.e. shell)
+.It Fl q
+Quiet mode.  When retrieving a variable only its data is printed.
+.It Fl s
+This option causes variables to be set system-wide and restricts retrievals
+to system-specific variables. Each jail have own ``local'' system-wide 
+variables.
+.It Fl u Ar user_name | user_id
+This option causes variables to be set on a per-user-id basis and restricts
+retrievals to user-specific variables for user specified as
+.Cm user_name Ns 
+ or
+.Cm user_id Ns .  Unprivileged user can set or read only own variables, and
+can't use this option. 
+.El
+.Sh RETURN VALUES
+The
+.Nm
+utility exits with one of the following values:
+.Bl -tag -width Ds
+.It 0
+No errors occured.
+.It 1
+A requested variable could not be found
+.It 2
+A requested variable could not be set
+.El
+.Sh EXAMPLES
+.Pp
+Create a parent variable fubar. This variable will then be inherited by 
+any future executions in this shell.
+.Pp
+.D1 Li "varsym -P fubar=xx"
+.Pp
+Create a user variable fubar.
+.Pp
+.D1 Li "varsym fubar=xx"
+.Pp
+List all user variables defined
+.Pp
+.D1 Li "varsym -a"
+.Pp
+List all variables defined anywhere
+.Pp
+.D1 Li "varsym -A"
+.Pp
+List unique variables defined
+.Pp
+.D1 Li "varsym -A|sort -u -t= -k1,1"
+.Sh SEE ALSO
+.Xr ln 1 ,
diff -U3 -N src/bin/varsym/varsym.c src/bin/varsym/varsym.c
--- src/bin/varsym/varsym.c	Wed Dec 31 19:00:00 1969
+++ src/bin/varsym/varsym.c	Fri Feb  3 13:05:44 2006
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
+ * All rights reserved.
+ *
+ * Modified by Andrey V. Elsukov <bu7cher@yandex.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $DragonFly: src/bin/varsym/varsym.c,v 1.4 2003/12/11 20:33:49 dillon Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/varsym.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <unistd.h>
+#include <pwd.h>
+
+static void	usage(void);
+static void	dumpvars(char *buf, int bytes, const char *prefix);
+static int	listvars(int level, const char *prefix);
+static int	jailed(void);
+
+int is_root = 0;
+int is_jail = 0;
+
+int
+main(int ac, char **av)
+{
+	int i, error;
+	size_t size;
+	const char* ops;
+	int mask =  VARSYM_ALL_MASK;
+	int level = VARSYM_USER;
+	int deleteOpt = 0;
+	int verboseOpt = 0;
+	int allOpt = 0;
+	int jid = 0;
+
+	is_root = (getuid() == 0);
+	is_jail = jailed();
+
+	if (is_jail) {
+		/* 
+		 * per-user varsyms not allowed in jail
+		 * So we remove from mask VARSYM_USER_MASK
+		 * and set VARSYM_SYS level as default.
+		 */
+		mask &= ~VARSYM_USER_MASK;
+		level = VARSYM_SYS;
+	}
+	if (is_jail || !is_root)
+		ops = "AadhPpqsv";
+	else
+		ops = "Aadhj:Ppqsu:v";
+	while ((i = getopt(ac, av, ops)) != -1) {
+		switch (i) {
+		case 'a':
+			allOpt = 1;
+			break;
+		case 'A':
+			allOpt = 2;
+			break;
+		case 'd':
+			deleteOpt = 1;
+			break;
+		case 'q':
+			verboseOpt = 0;
+			break;
+		case 'j':
+			jid = (int)strtol(optarg, NULL, 10);
+			error = jail_attach(jid);
+			if (error)
+				err(2, "jail_attach(): %d", jid);
+		case 'P':
+			level = VARSYM_PPROC;
+			mask = VARSYM_PPROC_MASK;
+			break;
+		case 'p':
+			level = VARSYM_PROC;
+			mask = VARSYM_PROC_MASK;
+			break;
+		case 's':
+			mask = VARSYM_SYS_MASK;
+			level = VARSYM_SYS;
+			break;
+		case 'u': {
+				uid_t	uid;
+				struct	passwd	*pw;
+				if (jid) {
+					usage();
+					return(1);
+				}
+				pw = getpwnam(optarg);
+				if (pw == NULL)
+					uid = (uid_t)strtol(optarg, NULL, 10);
+				else
+					uid = pw->pw_uid;
+				error = setuid(uid);
+				if (error)
+					err(1, "setuid()");
+				mask = VARSYM_USER_MASK;
+				level = VARSYM_USER;
+			}
+			break;
+		case 'v':
+			verboseOpt = 1;
+			break;
+		case 'h':
+		default:
+			usage();
+			return(1);
+		}
+	}
+	ac -= optind;
+	av += optind;
+
+	if (allOpt) {
+		if (allOpt > 1) {
+			/* process variables in order they are searched */
+			error = listvars(VARSYM_PROC, verboseOpt?"p":"");
+			error = listvars(VARSYM_PPROC, verboseOpt?"P":"");
+			error = listvars(VARSYM_USER, verboseOpt?"u":"");
+			error = listvars(VARSYM_SYS, verboseOpt?"s":"");
+		}
+		else {
+			error = listvars(level, "");
+		}
+		if (error < 0)
+			warn("varsym_list()");
+	} else if (ac == 0) {
+		usage();
+		return(1);
+	}
+
+	while (ac > 0) {
+		char *name = av[0];
+		char *data = strchr(name, '=');
+		char buf[MAXVARSYM_DATA];
+
+		if (data)
+			*data++ = 0;
+
+		if (deleteOpt) {
+			error = varsym_set(level, name, NULL);
+		}
+		else if (data) {
+			error = varsym_set(level, name, data);
+		}
+		else {
+			size = sizeof(buf);
+			error = varsym_get(mask, name, buf, (int *)&size);
+			if (error == 0 && size <= (int)sizeof(buf)) {
+				if (verboseOpt)
+					printf("%s=", name);
+				printf("%s\n", buf);
+			}
+		}
+		if (error != 0 && verboseOpt)
+			warn("%s", name);
+		av++; ac--;
+	}
+
+	return(0);
+}
+
+static void
+dumpvars(char *buf, int bytes, const char *prefix)
+{
+    int b;
+    int i;
+    char *vname = NULL;
+    char *vdata = NULL;
+
+    for (b = i = 0; i < bytes; ++i) {
+	if (buf[i] == 0) {
+	    if (vname == NULL) {
+			vname = buf + b;
+	    } else {
+			vdata = buf + b;
+			if (prefix && *prefix)
+				printf("%s:%s=%s\n", prefix, vname, vdata);
+			else
+				printf("%s=%s\n", vname, vdata);
+			vname = vdata = NULL;
+	    }
+	    b = i + 1;
+	}
+    }
+}
+
+static int
+listvars(int level, const char *prefix)
+{
+    int marker = 0;
+    char buf[1024];
+    int size = sizeof(buf);
+    int error=0;
+
+    for (;;) {
+	size = sizeof(buf);	
+	error = varsym_list(level, buf, (int *)&size,
+		&marker);
+	if (error < 0)		/* error occured */
+		break;
+	dumpvars(buf, size, prefix);
+	if (marker < 0)		/* no more vars */
+		break;
+    }
+    return(error);
+}
+
+static void
+usage(void)
+{
+	const char *str;
+	if (is_jail || !is_root)
+		str = "Usage: varsym: [-aApPqds] var[=data]\n";
+	else
+		str = "Usage: varsym: [-aApPqds] [-u user_id | user_name]\n" 
+			  "       [-j prison_id] var[=data]\n"
+			  "Note: -j and -u options is mutually exclusive\n";	
+	fprintf(stderr, str);
+}
+
+static int	
+jailed(void)
+{
+	int	j, error;
+	size_t size = sizeof(j);
+	error = sysctlbyname("security.jail.jailed", 
+					&j, &size, NULL, 0);
+	if (error)
+		err(2, "sysctlbyname()");
+	return(j);
+}
diff -U3 src/etc/defaults/rc.conf src/etc/defaults/rc.conf
--- src/etc/defaults/rc.conf	Sun Oct 15 14:22:17 2006
+++ src/etc/defaults/rc.conf	Thu Aug 30 18:59:03 2007
@@ -559,6 +559,8 @@
 newsyslog_enable="YES"	# Run newsyslog at startup.
 newsyslog_flags="-CN"	# Newsyslog flags to create marked files
 
+varsym_enable="NO"      # variant symlinks is disabled by default
+
 ##############################################################
 ### Jail Configuration #######################################
 ##############################################################
diff -U3 -N src/etc/rc.d/varsym src/etc/rc.d/varsym
--- src/etc/rc.d/varsym	Wed Dec 31 19:00:00 1969
+++ src/etc/rc.d/varsym	Thu Aug 30 18:59:03 2007
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+
+# PROVIDE: varsym
+
+. /etc/rc.subr
+
+name="varsym"
+rcvar=`set_rcvar`
+oid="vfs.varsym_enable"
+
+start_cmd="/sbin/sysctl $oid=1"
+stop_cmd="/sbin/sysctl $oid=0"
+
+#
+# handle status here since rc.subr doesn't allow it
+#
+case $1 in
+status)
+   case `/sbin/sysctl -n $oid` in
+   1)
+      echo $name is enabled.
+      return 0
+      ;;
+   0)
+      echo $name is disabled.
+   esac
+   return 1
+   ;;
+esac
+
+load_rc_config $name
+run_rc_command "$1"
diff -U3 -N src/etc/rc.d/Makefile src/etc/rc.d/Makefile
--- src/etc/rc.d/Makefile	2008-04-24 17:26:33.000000000 +0000
+++ src/etc/rc.d/Makefile	2008-04-24 17:26:41.000000000 +0000
@@ -36,7 +36,7 @@
 	syscons sysctl syslogd \
 	timed tmp \
 	ugidfw \
-	var virecover \
+	var varsym virecover \
 	watchdogd wpa_supplicant \
 	ypbind yppasswdd ypserv \
 	ypset ypupdated ypxfrd \
diff -U3 -N src/lib/libc/sys/Symbol.map.orig src/lib/libc/sys/Symbol.map
--- src/lib/libc/sys/Symbol.map.orig	2008-04-24 18:49:14.000000000 +0000
+++ src/lib/libc/sys/Symbol.map	2008-04-24 18:51:46.000000000 +0000
@@ -322,6 +322,9 @@
 	utrace;
 	uuidgen;
 	vadvise;
+	varsym_set;
+	varsym_get;
+	varsym_list;
 	wait4;
 	write;
 	writev;
@@ -956,6 +959,12 @@
 	__sys_uuidgen;
 	_vadvise;
 	__sys_vadvise;
+	_varsym_set;
+	__sys_varsym_set;
+	_varsym_get;
+	__sys_varsym_get;
+	_varsym_list;
+	__sys_varsym_list;
 	_wait4;
 	__sys_wait4;
 	_write;

[-- Attachment #3 --]
# $Id: Makefile,v 1.1 2008/03/01 02:09:35 sasdrq Exp $
#
# 

# a list of patch files for /usr source tree
PATCHES  != echo *.patch

# were we need to be to run this
INSTDIR = /usr
CURDIR != pwd

list:
	@echo "make patch" - to patch source and update sysctl.conf

patch:  patches sysent

# use the -p option of patch to cause any new directories to be created
patches:
	@cd ${INSTDIR}; list="${PATCHES}"; for patch in $$list; do \
	   file=${CURDIR}/$$patch;        \
	   if test -f $$file; then        \
	      /usr/bin/patch -p < $$file; \
	   else :; fi;                    \
	done

sysent:
	cd /usr/src/sys/kern && make sysent
	cd /usr/src/sys/compat/freebsd32 && make sysent


Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?48AE89DC.9080408>