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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, ©->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 @@ -901,3 +901,11 @@ 512 AUE_SHMCTL STD { int freebsd32_shmctl(int shmid, int cmd, \ struct shmid_ds32 *buf); } 513 AUE_LPATHCONF NOPROTO { int lpathconf(char *path, int name); } +514 AUE_NULL NOPROTO { int varsym_set(int level, \ + const char *name, \ + const char *data); } +515 AUE_NULL NOPROTO { int varsym_get(int mask, \ + const char *wild, \ + char *buf, int *size); } +516 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.259.2.1.2.1 diff -u -b -r1.259.2.1.2.1 syscalls.master --- src/sys/kern/syscalls.master.orig 2009-10-25 01:10:29.000000000 +0000 +++ src/sys/kern/syscalls.master 2009-12-07 15:56:51.000000000 +0000 @@ -911,5 +911,13 @@ 512 AUE_SHMCTL NOSTD { int shmctl(int shmid, int cmd, \ struct shmid_ds *buf); } 513 AUE_LPATHCONF STD { int lpathconf(char *path, int name); } +514 AUE_NULL STD { int varsym_set(int level, \ + const char *name, \ + const char *data); } +515 AUE_NULL STD { int varsym_get(int mask, \ + const char *wild, \ + char *buf, int *size); } +516 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.118.2.6.2.1 diff -u -b -r1.118.2.6.2.1 kern_jail.c --- src/sys/kern/kern_jail.c.orig 2009-10-25 01:10:29.000000000 +0000 +++ src/sys/kern/kern_jail.c 2009-12-07 18:30:38.000000000 +0000 @@ -501,6 +501,7 @@ unsigned pr_flags, ch_flags; unsigned pr_allow, ch_allow, tallow; char numbuf[12]; + char varsym_done=0; error = priv_check(td, PRIV_JAIL_SET); if (!error && (flags & JAIL_ATTACH)) @@ -1147,6 +1148,8 @@ pr->pr_parent = ppr; pr->pr_id = jid; + varsymset_init(&pr->pr_varsymset, NULL); + varsym_done = 1; /* Set some default values, and inherit some from the parent. */ if (name == NULL) @@ -1718,6 +1721,8 @@ prison_deref(pr, created ? PD_LOCKED | PD_LIST_XLOCKED : PD_DEREF | PD_LOCKED | PD_LIST_XLOCKED); + /* if we called prison_deref, we cleaned varsym already */ + varsym_done = 0; goto done_releroot; done_unlock_list: sx_xunlock(&allprison_lock); @@ -1745,6 +1750,8 @@ } } done_free: + if (varsym_done) + varsymset_clean(&pr->pr_varsymset); #ifdef INET free(ip4, M_PRISON); #endif @@ -2394,7 +2401,6 @@ static void prison_complete(void *context, int pending) { - prison_deref((struct prison *)context, 0); } @@ -2484,6 +2490,7 @@ #endif if (pr->pr_cpuset != NULL) cpuset_rel(pr->pr_cpuset); + varsymset_clean(&pr->pr_varsymset); osd_jail_exit(pr); free(pr, M_PRISON); Index: src/sys/kern/kern_resource.c =================================================================== RCS file: /ncvs/src/sys/kern/kern_resource.c,v retrieving revision 1.194.2.1.2.1 diff -u -b -r1.194.2.1.2.1 kern_resource.c --- src/sys/kern/kern_resource.c.orig 2009-10-25 01:10:29.000000000 +0000 +++ src/sys/kern/kern_resource.c 2009-12-07 16:32:58.000000000 +0000 @@ -1215,6 +1215,7 @@ uip->ui_uid = uid; mtx_init(&uip->ui_vmsize_mtx, "ui_vmsize", NULL, MTX_DEF); + varsymset_init(&uip->ui_varsymset, NULL); LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash); } } @@ -1274,6 +1275,7 @@ if (uip->ui_vmsize != 0) printf("freeing uidinfo: uid = %d, swapuse = %lld\n", uip->ui_uid, (unsigned long long)uip->ui_vmsize); + varsymset_clean(&uip->ui_varsymset); mtx_destroy(&uip->ui_vmsize_mtx); 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.50.2.2.2.1 diff -u -b -r1.50.2.2.2.1 jail.h --- src/sys/sys/jail.h.orig 2009-10-25 01:10:29.000000000 +0000 +++ src/sys/sys/jail.h 2009-12-07 16:41:40.000000000 +0000 @@ -132,6 +132,7 @@ #if defined(_KERNEL) || defined(_WANT_PRISON) #include +#include #define HOSTUUIDLEN 64 @@ -158,6 +159,7 @@ struct prison *pr_parent; /* (c) containing jail */ struct mtx pr_mtx; struct task pr_task; /* (d) destroy task */ + struct varsymset pr_varsymset; /* (p) prison varsyms */ struct osd pr_osd; /* (p) additional data */ struct cpuset *pr_cpuset; /* (p) cpuset */ struct vnet *pr_vnet; /* (c) network stack */ 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 #include +#include #include /* 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 #include +#include #endif /* @@ -93,6 +94,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 proc; 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 @@ -38,7 +38,8 @@ sleep \ stty \ sync \ test \ - uuidgen + uuidgen \ + varsym .if ${MK_RCMDS} != "no" _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 /* 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 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 +.\" All rights reserved. +.\" +.\" Modified by Andrey V. Elsukov +.\" +.\" 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 + * All rights reserved. + * + * Modified by Andrey V. Elsukov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + int 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, &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, &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;