xref: /dragonfly/sys/kern/kern_varsym.c (revision 063147e5)
198a7f915SMatthew Dillon /*
298a7f915SMatthew Dillon  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
398a7f915SMatthew Dillon  * All rights reserved.
498a7f915SMatthew Dillon  *
598a7f915SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
698a7f915SMatthew Dillon  * modification, are permitted provided that the following conditions
798a7f915SMatthew Dillon  * are met:
898a7f915SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
998a7f915SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1098a7f915SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1198a7f915SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
1298a7f915SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
1398a7f915SMatthew Dillon  *
1498a7f915SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1598a7f915SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1698a7f915SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1798a7f915SMatthew Dillon  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1898a7f915SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1998a7f915SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2098a7f915SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2198a7f915SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2298a7f915SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2398a7f915SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2498a7f915SMatthew Dillon  * SUCH DAMAGE.
2598a7f915SMatthew Dillon  *
26*063147e5SMatthew Dillon  * $DragonFly: src/sys/kern/kern_varsym.c,v 1.2 2003/11/09 20:29:59 dillon Exp $
2798a7f915SMatthew Dillon  */
2898a7f915SMatthew Dillon 
2998a7f915SMatthew Dillon /*
3098a7f915SMatthew Dillon  * This module implements variable storage and management for variant
3198a7f915SMatthew Dillon  * symlinks.  These variables may also be used for general purposes.
3298a7f915SMatthew Dillon  */
3398a7f915SMatthew Dillon 
3498a7f915SMatthew Dillon #include <sys/param.h>
3598a7f915SMatthew Dillon #include <sys/systm.h>
3698a7f915SMatthew Dillon #include <sys/kernel.h>
3798a7f915SMatthew Dillon #include <sys/ucred.h>
3898a7f915SMatthew Dillon #include <sys/resourcevar.h>
3998a7f915SMatthew Dillon #include <sys/proc.h>
4098a7f915SMatthew Dillon #include <sys/queue.h>
4198a7f915SMatthew Dillon #include <sys/sysctl.h>
4298a7f915SMatthew Dillon #include <sys/malloc.h>
4398a7f915SMatthew Dillon #include <sys/varsym.h>
4498a7f915SMatthew Dillon #include <sys/sysproto.h>
4598a7f915SMatthew Dillon 
4698a7f915SMatthew Dillon MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
4798a7f915SMatthew Dillon 
4898a7f915SMatthew Dillon struct varsymset	varsymset_sys;
4998a7f915SMatthew Dillon 
5098a7f915SMatthew Dillon /*
5198a7f915SMatthew Dillon  * Initialize the variant symlink subsystem
5298a7f915SMatthew Dillon  */
5398a7f915SMatthew Dillon static void
5498a7f915SMatthew Dillon varsym_sysinit(void *dummy)
5598a7f915SMatthew Dillon {
5698a7f915SMatthew Dillon     varsymset_init(&varsymset_sys, NULL);
5798a7f915SMatthew Dillon }
5898a7f915SMatthew Dillon SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL);
5998a7f915SMatthew Dillon 
6098a7f915SMatthew Dillon /*
61*063147e5SMatthew Dillon  * varsymreplace() - called from namei
62*063147e5SMatthew Dillon  *
63*063147e5SMatthew Dillon  *	Do variant symlink variable substitution
64*063147e5SMatthew Dillon  */
65*063147e5SMatthew Dillon int
66*063147e5SMatthew Dillon varsymreplace(char *cp, int linklen, int maxlen)
67*063147e5SMatthew Dillon {
68*063147e5SMatthew Dillon     int rlen;
69*063147e5SMatthew Dillon     int xlen;
70*063147e5SMatthew Dillon     int nlen;
71*063147e5SMatthew Dillon     int i;
72*063147e5SMatthew Dillon     varsym_t var;
73*063147e5SMatthew Dillon 
74*063147e5SMatthew Dillon     rlen = linklen;
75*063147e5SMatthew Dillon     while (linklen > 1) {
76*063147e5SMatthew Dillon 	if (cp[0] == '$' && cp[1] == '{') {
77*063147e5SMatthew Dillon 	    for (i = 2; i < linklen; ++i) {
78*063147e5SMatthew Dillon 		if (cp[i] == '}')
79*063147e5SMatthew Dillon 		    break;
80*063147e5SMatthew Dillon 	    }
81*063147e5SMatthew Dillon 	    if (i < linklen &&
82*063147e5SMatthew Dillon 		(var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
83*063147e5SMatthew Dillon 	    ) {
84*063147e5SMatthew Dillon 		xlen = i + 1;			/* bytes to strike */
85*063147e5SMatthew Dillon 		nlen = strlen(var->vs_data);	/* bytes to add */
86*063147e5SMatthew Dillon 		if (linklen + nlen - xlen >= maxlen) {
87*063147e5SMatthew Dillon 		    varsymdrop(var);
88*063147e5SMatthew Dillon 		    return(-1);
89*063147e5SMatthew Dillon 		}
90*063147e5SMatthew Dillon 		KKASSERT(linklen >= xlen);
91*063147e5SMatthew Dillon 		if (linklen != xlen)
92*063147e5SMatthew Dillon 		    bcopy(cp + xlen, cp + nlen, linklen - xlen);
93*063147e5SMatthew Dillon 		bcopy(var->vs_data, cp, nlen);
94*063147e5SMatthew Dillon 		linklen += nlen - xlen;	/* new relative length */
95*063147e5SMatthew Dillon 		rlen += nlen - xlen;	/* returned total length */
96*063147e5SMatthew Dillon 		cp += nlen;		/* adjust past replacement */
97*063147e5SMatthew Dillon 		linklen -= nlen;	/* adjust past replacement */
98*063147e5SMatthew Dillon 		maxlen -= nlen;		/* adjust past replacement */
99*063147e5SMatthew Dillon 	    } else {
100*063147e5SMatthew Dillon 		/*
101*063147e5SMatthew Dillon 		 * It's ok if i points to the '}', it will simply be
102*063147e5SMatthew Dillon 		 * skipped.  i could also have hit linklen.
103*063147e5SMatthew Dillon 		 */
104*063147e5SMatthew Dillon 		cp += i;
105*063147e5SMatthew Dillon 		linklen -= i;
106*063147e5SMatthew Dillon 		maxlen -= i;
107*063147e5SMatthew Dillon 	    }
108*063147e5SMatthew Dillon 	} else {
109*063147e5SMatthew Dillon 	    ++cp;
110*063147e5SMatthew Dillon 	    --linklen;
111*063147e5SMatthew Dillon 	    --maxlen;
112*063147e5SMatthew Dillon 	}
113*063147e5SMatthew Dillon     }
114*063147e5SMatthew Dillon     return(rlen);
115*063147e5SMatthew Dillon }
116*063147e5SMatthew Dillon 
117*063147e5SMatthew Dillon /*
11898a7f915SMatthew Dillon  * varsym_set() system call
11998a7f915SMatthew Dillon  *
12098a7f915SMatthew Dillon  * (int level, const char *name, const char *data)
12198a7f915SMatthew Dillon  */
12298a7f915SMatthew Dillon int
12398a7f915SMatthew Dillon varsym_set(struct varsym_set_args *uap)
12498a7f915SMatthew Dillon {
12598a7f915SMatthew Dillon     char name[MAXVARSYM_NAME];
12698a7f915SMatthew Dillon     char *buf;
12798a7f915SMatthew Dillon     int error;
12898a7f915SMatthew Dillon 
12998a7f915SMatthew Dillon     if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
13098a7f915SMatthew Dillon 	goto done2;
13198a7f915SMatthew Dillon     buf = malloc(MAXVARSYM_DATA, M_TEMP, 0);
13298a7f915SMatthew Dillon     if (uap->data &&
13398a7f915SMatthew Dillon 	(error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
13498a7f915SMatthew Dillon     {
13598a7f915SMatthew Dillon 	goto done1;
13698a7f915SMatthew Dillon     }
13798a7f915SMatthew Dillon     switch(uap->level) {
13898a7f915SMatthew Dillon     case VARSYM_SYS:
13998a7f915SMatthew Dillon 	if ((error = suser(curthread)) != 0)
14098a7f915SMatthew Dillon 	    break;
14198a7f915SMatthew Dillon 	/* XXX implement per-jail sys */
14298a7f915SMatthew Dillon 	/* fall through */
14398a7f915SMatthew Dillon     case VARSYM_USER:
14498a7f915SMatthew Dillon 	/* XXX check jail / implement per-jail user */
14598a7f915SMatthew Dillon 	/* fall through */
14698a7f915SMatthew Dillon     case VARSYM_PROC:
14798a7f915SMatthew Dillon 	if (uap->data) {
14898a7f915SMatthew Dillon 	    (void)varsymmake(uap->level, name, NULL);
14998a7f915SMatthew Dillon 	    error = varsymmake(uap->level, name, buf);
15098a7f915SMatthew Dillon 	} else {
15198a7f915SMatthew Dillon 	    error = varsymmake(uap->level, name, NULL);
15298a7f915SMatthew Dillon 	}
15398a7f915SMatthew Dillon 	break;
15498a7f915SMatthew Dillon     }
15598a7f915SMatthew Dillon done1:
15698a7f915SMatthew Dillon     free(buf, M_TEMP);
15798a7f915SMatthew Dillon done2:
15898a7f915SMatthew Dillon     return(error);
15998a7f915SMatthew Dillon }
16098a7f915SMatthew Dillon 
16198a7f915SMatthew Dillon /*
16298a7f915SMatthew Dillon  * varsym_get() system call
16398a7f915SMatthew Dillon  *
16498a7f915SMatthew Dillon  * (int mask, const char *wild, char *buf, int bufsize)
16598a7f915SMatthew Dillon  */
16698a7f915SMatthew Dillon int
16798a7f915SMatthew Dillon varsym_get(struct varsym_get_args *uap)
16898a7f915SMatthew Dillon {
16998a7f915SMatthew Dillon     char wild[MAXVARSYM_NAME];
17098a7f915SMatthew Dillon     varsym_t sym;
17198a7f915SMatthew Dillon     int error;
17298a7f915SMatthew Dillon     int dlen;
17398a7f915SMatthew Dillon 
17498a7f915SMatthew Dillon     if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
17598a7f915SMatthew Dillon 	goto done;
17698a7f915SMatthew Dillon     sym = varsymfind(uap->mask, wild, strlen(wild));
17798a7f915SMatthew Dillon     if (sym == NULL) {
17898a7f915SMatthew Dillon 	error = ENOENT;
17998a7f915SMatthew Dillon 	goto done;
18098a7f915SMatthew Dillon     }
18198a7f915SMatthew Dillon     dlen = strlen(sym->vs_data);
18298a7f915SMatthew Dillon     if (dlen < uap->bufsize) {
18398a7f915SMatthew Dillon 	copyout(sym->vs_data, uap->buf, dlen + 1);
18498a7f915SMatthew Dillon     } else if (uap->bufsize) {
18598a7f915SMatthew Dillon 	copyout("", uap->buf, 1);
18698a7f915SMatthew Dillon     }
18798a7f915SMatthew Dillon     uap->sysmsg_result = dlen + 1;
18898a7f915SMatthew Dillon     varsymdrop(sym);
18998a7f915SMatthew Dillon done:
19098a7f915SMatthew Dillon     return(error);
19198a7f915SMatthew Dillon }
19298a7f915SMatthew Dillon 
19398a7f915SMatthew Dillon /*
19498a7f915SMatthew Dillon  * Lookup a variant symlink.  XXX use a hash table.
19598a7f915SMatthew Dillon  */
19698a7f915SMatthew Dillon static
19798a7f915SMatthew Dillon struct varsyment *
19898a7f915SMatthew Dillon varsymlookup(struct varsymset *vss, const char *name, int namelen)
19998a7f915SMatthew Dillon {
20098a7f915SMatthew Dillon     struct varsyment *ve;
20198a7f915SMatthew Dillon 
20298a7f915SMatthew Dillon     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
20398a7f915SMatthew Dillon 	varsym_t var = ve->ve_sym;
20498a7f915SMatthew Dillon 	if (var->vs_namelen == namelen &&
20598a7f915SMatthew Dillon 	    bcmp(name, var->vs_name, namelen) == 0
20698a7f915SMatthew Dillon 	) {
20798a7f915SMatthew Dillon 	    return(ve);
20898a7f915SMatthew Dillon 	}
20998a7f915SMatthew Dillon     }
21098a7f915SMatthew Dillon     return(NULL);
21198a7f915SMatthew Dillon }
21298a7f915SMatthew Dillon 
21398a7f915SMatthew Dillon varsym_t
21498a7f915SMatthew Dillon varsymfind(int mask, const char *name, int namelen)
21598a7f915SMatthew Dillon {
21698a7f915SMatthew Dillon     struct proc *p;
21798a7f915SMatthew Dillon     struct varsyment *ve = NULL;
21898a7f915SMatthew Dillon     varsym_t sym;
21998a7f915SMatthew Dillon 
22098a7f915SMatthew Dillon     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) {
22198a7f915SMatthew Dillon 	if (mask & VARSYM_PROC_MASK)
22298a7f915SMatthew Dillon 	    ve = varsymlookup(&p->p_varsymset, name, namelen);
22398a7f915SMatthew Dillon 	if (ve == NULL && (mask & VARSYM_USER_MASK))
22498a7f915SMatthew Dillon 	    ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen);
22598a7f915SMatthew Dillon     }
22698a7f915SMatthew Dillon     if (ve == NULL && (mask & VARSYM_SYS_MASK))
22798a7f915SMatthew Dillon 	ve = varsymlookup(&varsymset_sys, name, namelen);
22898a7f915SMatthew Dillon     if (ve) {
22998a7f915SMatthew Dillon 	sym = ve->ve_sym;
23098a7f915SMatthew Dillon 	++sym->vs_refs;
23198a7f915SMatthew Dillon 	return(sym);
23298a7f915SMatthew Dillon     } else {
23398a7f915SMatthew Dillon 	return(NULL);
23498a7f915SMatthew Dillon     }
23598a7f915SMatthew Dillon }
23698a7f915SMatthew Dillon 
23798a7f915SMatthew Dillon int
23898a7f915SMatthew Dillon varsymmake(int level, const char *name, const char *data)
23998a7f915SMatthew Dillon {
24098a7f915SMatthew Dillon     struct varsymset *vss = NULL;
24198a7f915SMatthew Dillon     struct varsyment *ve;
24298a7f915SMatthew Dillon     struct proc *p = curproc;
24398a7f915SMatthew Dillon     varsym_t sym;
24498a7f915SMatthew Dillon     int namelen = strlen(name);
24598a7f915SMatthew Dillon     int datalen;
24698a7f915SMatthew Dillon     int error;
24798a7f915SMatthew Dillon 
24898a7f915SMatthew Dillon     switch(level) {
24998a7f915SMatthew Dillon     case VARSYM_PROC:
25098a7f915SMatthew Dillon 	if (p)
25198a7f915SMatthew Dillon 	    vss = &p->p_varsymset;
25298a7f915SMatthew Dillon 	break;
25398a7f915SMatthew Dillon     case VARSYM_USER:
25498a7f915SMatthew Dillon 	if (p)
25598a7f915SMatthew Dillon 	    vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
25698a7f915SMatthew Dillon 	break;
25798a7f915SMatthew Dillon     case VARSYM_SYS:
25898a7f915SMatthew Dillon 	vss = &varsymset_sys;
25998a7f915SMatthew Dillon 	break;
26098a7f915SMatthew Dillon     }
26198a7f915SMatthew Dillon     if (vss == NULL) {
26298a7f915SMatthew Dillon 	error = EINVAL;
26398a7f915SMatthew Dillon     } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
26498a7f915SMatthew Dillon 	error = E2BIG;
26598a7f915SMatthew Dillon     } else if (data) {
26698a7f915SMatthew Dillon 	datalen = strlen(data);
26798a7f915SMatthew Dillon 	ve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO);
26898a7f915SMatthew Dillon 	sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, 0);
26998a7f915SMatthew Dillon 	ve->ve_sym = sym;
27098a7f915SMatthew Dillon 	sym->vs_refs = 1;
27198a7f915SMatthew Dillon 	sym->vs_namelen = namelen;
27298a7f915SMatthew Dillon 	sym->vs_name = (char *)(sym + 1);
27398a7f915SMatthew Dillon 	sym->vs_data = sym->vs_name + namelen + 1;
27498a7f915SMatthew Dillon 	strcpy(sym->vs_name, name);
27598a7f915SMatthew Dillon 	strcpy(sym->vs_data, data);
27698a7f915SMatthew Dillon 	TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
27798a7f915SMatthew Dillon 	vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
27898a7f915SMatthew Dillon 	error = 0;
27998a7f915SMatthew Dillon     } else {
28098a7f915SMatthew Dillon 	if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
28198a7f915SMatthew Dillon 	    TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
28298a7f915SMatthew Dillon 	    vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
28398a7f915SMatthew Dillon 	    varsymdrop(ve->ve_sym);
28498a7f915SMatthew Dillon 	    free(ve, M_VARSYM);
28598a7f915SMatthew Dillon 	    error = 0;
28698a7f915SMatthew Dillon 	} else {
28798a7f915SMatthew Dillon 	    error = ENOENT;
28898a7f915SMatthew Dillon 	}
28998a7f915SMatthew Dillon     }
29098a7f915SMatthew Dillon     return(error);
29198a7f915SMatthew Dillon }
29298a7f915SMatthew Dillon 
29398a7f915SMatthew Dillon void
29498a7f915SMatthew Dillon varsymdrop(varsym_t sym)
29598a7f915SMatthew Dillon {
29698a7f915SMatthew Dillon     KKASSERT(sym->vs_refs > 0);
29798a7f915SMatthew Dillon     if (--sym->vs_refs == 0) {
29898a7f915SMatthew Dillon 	free(sym, M_VARSYM);
29998a7f915SMatthew Dillon     }
30098a7f915SMatthew Dillon }
30198a7f915SMatthew Dillon 
30298a7f915SMatthew Dillon static void
30398a7f915SMatthew Dillon varsymdup(struct varsymset *vss, struct varsyment *ve)
30498a7f915SMatthew Dillon {
30598a7f915SMatthew Dillon     struct varsyment *nve;
30698a7f915SMatthew Dillon 
30798a7f915SMatthew Dillon     nve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO);
30898a7f915SMatthew Dillon     nve->ve_sym = ve->ve_sym;
30998a7f915SMatthew Dillon     ++nve->ve_sym->vs_refs;
31098a7f915SMatthew Dillon     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
31198a7f915SMatthew Dillon }
31298a7f915SMatthew Dillon 
31398a7f915SMatthew Dillon void
31498a7f915SMatthew Dillon varsymset_init(struct varsymset *vss, struct varsymset *copy)
31598a7f915SMatthew Dillon {
31698a7f915SMatthew Dillon     struct varsyment *ve;
31798a7f915SMatthew Dillon 
31898a7f915SMatthew Dillon     TAILQ_INIT(&vss->vx_queue);
31998a7f915SMatthew Dillon     if (copy) {
32098a7f915SMatthew Dillon 	TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
32198a7f915SMatthew Dillon 	    varsymdup(vss, ve);
32298a7f915SMatthew Dillon 	}
32398a7f915SMatthew Dillon 	vss->vx_setsize = copy->vx_setsize;
32498a7f915SMatthew Dillon     }
32598a7f915SMatthew Dillon }
32698a7f915SMatthew Dillon 
32798a7f915SMatthew Dillon void
32898a7f915SMatthew Dillon varsymset_clean(struct varsymset *vss)
32998a7f915SMatthew Dillon {
33098a7f915SMatthew Dillon     struct varsyment *ve;
33198a7f915SMatthew Dillon 
33298a7f915SMatthew Dillon     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
33398a7f915SMatthew Dillon 	TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
33498a7f915SMatthew Dillon 	varsymdrop(ve->ve_sym);
33598a7f915SMatthew Dillon 	free(ve, M_VARSYM);
33698a7f915SMatthew Dillon     }
33798a7f915SMatthew Dillon     vss->vx_setsize = 0;
33898a7f915SMatthew Dillon }
33998a7f915SMatthew Dillon 
340