1 /* 2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $DragonFly: src/sys/kern/kern_varsym.c,v 1.2 2003/11/09 20:29:59 dillon Exp $ 27 */ 28 29 /* 30 * This module implements variable storage and management for variant 31 * symlinks. These variables may also be used for general purposes. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/ucred.h> 38 #include <sys/resourcevar.h> 39 #include <sys/proc.h> 40 #include <sys/queue.h> 41 #include <sys/sysctl.h> 42 #include <sys/malloc.h> 43 #include <sys/varsym.h> 44 #include <sys/sysproto.h> 45 46 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks"); 47 48 struct varsymset varsymset_sys; 49 50 /* 51 * Initialize the variant symlink subsystem 52 */ 53 static void 54 varsym_sysinit(void *dummy) 55 { 56 varsymset_init(&varsymset_sys, NULL); 57 } 58 SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL); 59 60 /* 61 * varsymreplace() - called from namei 62 * 63 * Do variant symlink variable substitution 64 */ 65 int 66 varsymreplace(char *cp, int linklen, int maxlen) 67 { 68 int rlen; 69 int xlen; 70 int nlen; 71 int i; 72 varsym_t var; 73 74 rlen = linklen; 75 while (linklen > 1) { 76 if (cp[0] == '$' && cp[1] == '{') { 77 for (i = 2; i < linklen; ++i) { 78 if (cp[i] == '}') 79 break; 80 } 81 if (i < linklen && 82 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL 83 ) { 84 xlen = i + 1; /* bytes to strike */ 85 nlen = strlen(var->vs_data); /* bytes to add */ 86 if (linklen + nlen - xlen >= maxlen) { 87 varsymdrop(var); 88 return(-1); 89 } 90 KKASSERT(linklen >= xlen); 91 if (linklen != xlen) 92 bcopy(cp + xlen, cp + nlen, linklen - xlen); 93 bcopy(var->vs_data, cp, nlen); 94 linklen += nlen - xlen; /* new relative length */ 95 rlen += nlen - xlen; /* returned total length */ 96 cp += nlen; /* adjust past replacement */ 97 linklen -= nlen; /* adjust past replacement */ 98 maxlen -= nlen; /* adjust past replacement */ 99 } else { 100 /* 101 * It's ok if i points to the '}', it will simply be 102 * skipped. i could also have hit linklen. 103 */ 104 cp += i; 105 linklen -= i; 106 maxlen -= i; 107 } 108 } else { 109 ++cp; 110 --linklen; 111 --maxlen; 112 } 113 } 114 return(rlen); 115 } 116 117 /* 118 * varsym_set() system call 119 * 120 * (int level, const char *name, const char *data) 121 */ 122 int 123 varsym_set(struct varsym_set_args *uap) 124 { 125 char name[MAXVARSYM_NAME]; 126 char *buf; 127 int error; 128 129 if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0) 130 goto done2; 131 buf = malloc(MAXVARSYM_DATA, M_TEMP, 0); 132 if (uap->data && 133 (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0) 134 { 135 goto done1; 136 } 137 switch(uap->level) { 138 case VARSYM_SYS: 139 if ((error = suser(curthread)) != 0) 140 break; 141 /* XXX implement per-jail sys */ 142 /* fall through */ 143 case VARSYM_USER: 144 /* XXX check jail / implement per-jail user */ 145 /* fall through */ 146 case VARSYM_PROC: 147 if (uap->data) { 148 (void)varsymmake(uap->level, name, NULL); 149 error = varsymmake(uap->level, name, buf); 150 } else { 151 error = varsymmake(uap->level, name, NULL); 152 } 153 break; 154 } 155 done1: 156 free(buf, M_TEMP); 157 done2: 158 return(error); 159 } 160 161 /* 162 * varsym_get() system call 163 * 164 * (int mask, const char *wild, char *buf, int bufsize) 165 */ 166 int 167 varsym_get(struct varsym_get_args *uap) 168 { 169 char wild[MAXVARSYM_NAME]; 170 varsym_t sym; 171 int error; 172 int dlen; 173 174 if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0) 175 goto done; 176 sym = varsymfind(uap->mask, wild, strlen(wild)); 177 if (sym == NULL) { 178 error = ENOENT; 179 goto done; 180 } 181 dlen = strlen(sym->vs_data); 182 if (dlen < uap->bufsize) { 183 copyout(sym->vs_data, uap->buf, dlen + 1); 184 } else if (uap->bufsize) { 185 copyout("", uap->buf, 1); 186 } 187 uap->sysmsg_result = dlen + 1; 188 varsymdrop(sym); 189 done: 190 return(error); 191 } 192 193 /* 194 * Lookup a variant symlink. XXX use a hash table. 195 */ 196 static 197 struct varsyment * 198 varsymlookup(struct varsymset *vss, const char *name, int namelen) 199 { 200 struct varsyment *ve; 201 202 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 203 varsym_t var = ve->ve_sym; 204 if (var->vs_namelen == namelen && 205 bcmp(name, var->vs_name, namelen) == 0 206 ) { 207 return(ve); 208 } 209 } 210 return(NULL); 211 } 212 213 varsym_t 214 varsymfind(int mask, const char *name, int namelen) 215 { 216 struct proc *p; 217 struct varsyment *ve = NULL; 218 varsym_t sym; 219 220 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) { 221 if (mask & VARSYM_PROC_MASK) 222 ve = varsymlookup(&p->p_varsymset, name, namelen); 223 if (ve == NULL && (mask & VARSYM_USER_MASK)) 224 ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen); 225 } 226 if (ve == NULL && (mask & VARSYM_SYS_MASK)) 227 ve = varsymlookup(&varsymset_sys, name, namelen); 228 if (ve) { 229 sym = ve->ve_sym; 230 ++sym->vs_refs; 231 return(sym); 232 } else { 233 return(NULL); 234 } 235 } 236 237 int 238 varsymmake(int level, const char *name, const char *data) 239 { 240 struct varsymset *vss = NULL; 241 struct varsyment *ve; 242 struct proc *p = curproc; 243 varsym_t sym; 244 int namelen = strlen(name); 245 int datalen; 246 int error; 247 248 switch(level) { 249 case VARSYM_PROC: 250 if (p) 251 vss = &p->p_varsymset; 252 break; 253 case VARSYM_USER: 254 if (p) 255 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 256 break; 257 case VARSYM_SYS: 258 vss = &varsymset_sys; 259 break; 260 } 261 if (vss == NULL) { 262 error = EINVAL; 263 } else if (data && vss->vx_setsize >= MAXVARSYM_SET) { 264 error = E2BIG; 265 } else if (data) { 266 datalen = strlen(data); 267 ve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO); 268 sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, 0); 269 ve->ve_sym = sym; 270 sym->vs_refs = 1; 271 sym->vs_namelen = namelen; 272 sym->vs_name = (char *)(sym + 1); 273 sym->vs_data = sym->vs_name + namelen + 1; 274 strcpy(sym->vs_name, name); 275 strcpy(sym->vs_data, data); 276 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry); 277 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8; 278 error = 0; 279 } else { 280 if ((ve = varsymlookup(vss, name, namelen)) != NULL) { 281 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 282 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8; 283 varsymdrop(ve->ve_sym); 284 free(ve, M_VARSYM); 285 error = 0; 286 } else { 287 error = ENOENT; 288 } 289 } 290 return(error); 291 } 292 293 void 294 varsymdrop(varsym_t sym) 295 { 296 KKASSERT(sym->vs_refs > 0); 297 if (--sym->vs_refs == 0) { 298 free(sym, M_VARSYM); 299 } 300 } 301 302 static void 303 varsymdup(struct varsymset *vss, struct varsyment *ve) 304 { 305 struct varsyment *nve; 306 307 nve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO); 308 nve->ve_sym = ve->ve_sym; 309 ++nve->ve_sym->vs_refs; 310 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry); 311 } 312 313 void 314 varsymset_init(struct varsymset *vss, struct varsymset *copy) 315 { 316 struct varsyment *ve; 317 318 TAILQ_INIT(&vss->vx_queue); 319 if (copy) { 320 TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) { 321 varsymdup(vss, ve); 322 } 323 vss->vx_setsize = copy->vx_setsize; 324 } 325 } 326 327 void 328 varsymset_clean(struct varsymset *vss) 329 { 330 struct varsyment *ve; 331 332 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) { 333 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 334 varsymdrop(ve->ve_sym); 335 free(ve, M_VARSYM); 336 } 337 vss->vx_setsize = 0; 338 } 339 340