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.3 2003/11/10 23:58:57 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 * varsym_list() system call 195 * 196 * (int level, char *buf, int maxsize, int *marker) 197 */ 198 int 199 varsym_list(struct varsym_list_args *uap) 200 { 201 struct varsymset *vss; 202 struct varsyment *ve; 203 struct proc *p; 204 int i; 205 int error; 206 int bytes; 207 int earlyterm; 208 int marker; 209 210 /* 211 * Get the marker from userspace. 212 */ 213 if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0) 214 goto done; 215 216 /* 217 * Figure out the varsym set. 218 */ 219 p = curproc; 220 vss = NULL; 221 222 switch (uap->level) { 223 case VARSYM_PROC: 224 if (p) 225 vss = &p->p_varsymset; 226 break; 227 case VARSYM_USER: 228 if (p) 229 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 230 break; 231 case VARSYM_SYS: 232 vss = &varsymset_sys; 233 break; 234 } 235 if (vss == NULL) { 236 error = EINVAL; 237 goto done; 238 } 239 240 /* 241 * Loop through the variables and dump them to uap->buf 242 */ 243 i = 0; 244 bytes = 0; 245 earlyterm = 0; 246 247 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 248 varsym_t sym = ve->ve_sym; 249 int namelen = strlen(sym->vs_name); 250 int datalen = strlen(sym->vs_data); 251 int totlen = namelen + datalen + 2; 252 253 /* 254 * Skip to our index point 255 */ 256 if (i < marker) { 257 ++i; 258 continue; 259 } 260 261 /* 262 * Stop if there is insufficient space in the user buffer. 263 * If we haven't stored anything yet return EOVERFLOW. 264 * Note that the marker index (i) does not change. 265 */ 266 if (bytes + totlen > uap->maxsize) { 267 if (bytes == 0) 268 error = EOVERFLOW; 269 earlyterm = 1; 270 break; 271 } 272 273 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1); 274 if (error == 0) { 275 bytes += namelen + 1; 276 error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1); 277 if (error == 0) 278 bytes += datalen + 1; 279 else 280 bytes -= namelen + 1; /* revert if error */ 281 } 282 if (error) { 283 earlyterm = 1; 284 break; 285 } 286 ++i; 287 } 288 289 /* 290 * Save the marker back. If no error occured and earlyterm is clear 291 * the marker is set to -1 indicating that the variable list has been 292 * exhausted. If no error occured the number of bytes loaded into 293 * the buffer will be returned, otherwise the syscall code returns -1. 294 */ 295 if (error == 0 && earlyterm == 0) 296 marker = -1; 297 else 298 marker = i; 299 if (error == 0) 300 error = copyout(&marker, uap->marker, sizeof(marker)); 301 uap->sysmsg_result = bytes; 302 done: 303 return(error); 304 } 305 306 /* 307 * Lookup a variant symlink. XXX use a hash table. 308 */ 309 static 310 struct varsyment * 311 varsymlookup(struct varsymset *vss, const char *name, int namelen) 312 { 313 struct varsyment *ve; 314 315 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 316 varsym_t var = ve->ve_sym; 317 if (var->vs_namelen == namelen && 318 bcmp(name, var->vs_name, namelen) == 0 319 ) { 320 return(ve); 321 } 322 } 323 return(NULL); 324 } 325 326 varsym_t 327 varsymfind(int mask, const char *name, int namelen) 328 { 329 struct proc *p; 330 struct varsyment *ve = NULL; 331 varsym_t sym; 332 333 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) { 334 if (mask & VARSYM_PROC_MASK) 335 ve = varsymlookup(&p->p_varsymset, name, namelen); 336 if (ve == NULL && (mask & VARSYM_USER_MASK)) 337 ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen); 338 } 339 if (ve == NULL && (mask & VARSYM_SYS_MASK)) 340 ve = varsymlookup(&varsymset_sys, name, namelen); 341 if (ve) { 342 sym = ve->ve_sym; 343 ++sym->vs_refs; 344 return(sym); 345 } else { 346 return(NULL); 347 } 348 } 349 350 int 351 varsymmake(int level, const char *name, const char *data) 352 { 353 struct varsymset *vss = NULL; 354 struct varsyment *ve; 355 struct proc *p = curproc; 356 varsym_t sym; 357 int namelen = strlen(name); 358 int datalen; 359 int error; 360 361 switch(level) { 362 case VARSYM_PROC: 363 if (p) 364 vss = &p->p_varsymset; 365 break; 366 case VARSYM_USER: 367 if (p) 368 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 369 break; 370 case VARSYM_SYS: 371 vss = &varsymset_sys; 372 break; 373 } 374 if (vss == NULL) { 375 error = EINVAL; 376 } else if (data && vss->vx_setsize >= MAXVARSYM_SET) { 377 error = E2BIG; 378 } else if (data) { 379 datalen = strlen(data); 380 ve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO); 381 sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, 0); 382 ve->ve_sym = sym; 383 sym->vs_refs = 1; 384 sym->vs_namelen = namelen; 385 sym->vs_name = (char *)(sym + 1); 386 sym->vs_data = sym->vs_name + namelen + 1; 387 strcpy(sym->vs_name, name); 388 strcpy(sym->vs_data, data); 389 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry); 390 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8; 391 error = 0; 392 } else { 393 if ((ve = varsymlookup(vss, name, namelen)) != NULL) { 394 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 395 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8; 396 varsymdrop(ve->ve_sym); 397 free(ve, M_VARSYM); 398 error = 0; 399 } else { 400 error = ENOENT; 401 } 402 } 403 return(error); 404 } 405 406 void 407 varsymdrop(varsym_t sym) 408 { 409 KKASSERT(sym->vs_refs > 0); 410 if (--sym->vs_refs == 0) { 411 free(sym, M_VARSYM); 412 } 413 } 414 415 static void 416 varsymdup(struct varsymset *vss, struct varsyment *ve) 417 { 418 struct varsyment *nve; 419 420 nve = malloc(sizeof(struct varsyment), M_VARSYM, M_ZERO); 421 nve->ve_sym = ve->ve_sym; 422 ++nve->ve_sym->vs_refs; 423 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry); 424 } 425 426 void 427 varsymset_init(struct varsymset *vss, struct varsymset *copy) 428 { 429 struct varsyment *ve; 430 431 TAILQ_INIT(&vss->vx_queue); 432 if (copy) { 433 TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) { 434 varsymdup(vss, ve); 435 } 436 vss->vx_setsize = copy->vx_setsize; 437 } 438 } 439 440 void 441 varsymset_clean(struct varsymset *vss) 442 { 443 struct varsyment *ve; 444 445 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) { 446 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 447 varsymdrop(ve->ve_sym); 448 free(ve, M_VARSYM); 449 } 450 vss->vx_setsize = 0; 451 } 452 453