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, ©->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