1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/kern/kern_varsym.c,v 1.5 2004/07/16 05:51:10 dillon Exp $ 35 */ 36 37 /* 38 * This module implements variable storage and management for variant 39 * symlinks. These variables may also be used for general purposes. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/ucred.h> 46 #include <sys/resourcevar.h> 47 #include <sys/proc.h> 48 #include <sys/queue.h> 49 #include <sys/sysctl.h> 50 #include <sys/malloc.h> 51 #include <sys/varsym.h> 52 #include <sys/sysproto.h> 53 54 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks"); 55 56 struct varsymset varsymset_sys; 57 58 /* 59 * Initialize the variant symlink subsystem 60 */ 61 static void 62 varsym_sysinit(void *dummy) 63 { 64 varsymset_init(&varsymset_sys, NULL); 65 } 66 SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL); 67 68 /* 69 * varsymreplace() - called from namei 70 * 71 * Do variant symlink variable substitution 72 */ 73 int 74 varsymreplace(char *cp, int linklen, int maxlen) 75 { 76 int rlen; 77 int xlen; 78 int nlen; 79 int i; 80 varsym_t var; 81 82 rlen = linklen; 83 while (linklen > 1) { 84 if (cp[0] == '$' && cp[1] == '{') { 85 for (i = 2; i < linklen; ++i) { 86 if (cp[i] == '}') 87 break; 88 } 89 if (i < linklen && 90 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL 91 ) { 92 xlen = i + 1; /* bytes to strike */ 93 nlen = strlen(var->vs_data); /* bytes to add */ 94 if (linklen + nlen - xlen >= maxlen) { 95 varsymdrop(var); 96 return(-1); 97 } 98 KKASSERT(linklen >= xlen); 99 if (linklen != xlen) 100 bcopy(cp + xlen, cp + nlen, linklen - xlen); 101 bcopy(var->vs_data, cp, nlen); 102 linklen += nlen - xlen; /* new relative length */ 103 rlen += nlen - xlen; /* returned total length */ 104 cp += nlen; /* adjust past replacement */ 105 linklen -= nlen; /* adjust past replacement */ 106 maxlen -= nlen; /* adjust past replacement */ 107 } else { 108 /* 109 * It's ok if i points to the '}', it will simply be 110 * skipped. i could also have hit linklen. 111 */ 112 cp += i; 113 linklen -= i; 114 maxlen -= i; 115 } 116 } else { 117 ++cp; 118 --linklen; 119 --maxlen; 120 } 121 } 122 return(rlen); 123 } 124 125 /* 126 * varsym_set() system call 127 * 128 * (int level, const char *name, const char *data) 129 */ 130 int 131 varsym_set(struct varsym_set_args *uap) 132 { 133 char name[MAXVARSYM_NAME]; 134 char *buf; 135 int error; 136 137 if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0) 138 goto done2; 139 buf = malloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK); 140 if (uap->data && 141 (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0) 142 { 143 goto done1; 144 } 145 switch(uap->level) { 146 case VARSYM_SYS: 147 if ((error = suser(curthread)) != 0) 148 break; 149 /* XXX implement per-jail sys */ 150 /* fall through */ 151 case VARSYM_USER: 152 /* XXX check jail / implement per-jail user */ 153 /* fall through */ 154 case VARSYM_PROC: 155 if (uap->data) { 156 (void)varsymmake(uap->level, name, NULL); 157 error = varsymmake(uap->level, name, buf); 158 } else { 159 error = varsymmake(uap->level, name, NULL); 160 } 161 break; 162 } 163 done1: 164 free(buf, M_TEMP); 165 done2: 166 return(error); 167 } 168 169 /* 170 * varsym_get() system call 171 * 172 * (int mask, const char *wild, char *buf, int bufsize) 173 */ 174 int 175 varsym_get(struct varsym_get_args *uap) 176 { 177 char wild[MAXVARSYM_NAME]; 178 varsym_t sym; 179 int error; 180 int dlen; 181 182 if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0) 183 goto done; 184 sym = varsymfind(uap->mask, wild, strlen(wild)); 185 if (sym == NULL) { 186 error = ENOENT; 187 goto done; 188 } 189 dlen = strlen(sym->vs_data); 190 if (dlen < uap->bufsize) { 191 copyout(sym->vs_data, uap->buf, dlen + 1); 192 } else if (uap->bufsize) { 193 copyout("", uap->buf, 1); 194 } 195 uap->sysmsg_result = dlen + 1; 196 varsymdrop(sym); 197 done: 198 return(error); 199 } 200 201 /* 202 * varsym_list() system call 203 * 204 * (int level, char *buf, int maxsize, int *marker) 205 */ 206 int 207 varsym_list(struct varsym_list_args *uap) 208 { 209 struct varsymset *vss; 210 struct varsyment *ve; 211 struct proc *p; 212 int i; 213 int error; 214 int bytes; 215 int earlyterm; 216 int marker; 217 218 /* 219 * Get the marker from userspace. 220 */ 221 if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0) 222 goto done; 223 224 /* 225 * Figure out the varsym set. 226 */ 227 p = curproc; 228 vss = NULL; 229 230 switch (uap->level) { 231 case VARSYM_PROC: 232 if (p) 233 vss = &p->p_varsymset; 234 break; 235 case VARSYM_USER: 236 if (p) 237 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 238 break; 239 case VARSYM_SYS: 240 vss = &varsymset_sys; 241 break; 242 } 243 if (vss == NULL) { 244 error = EINVAL; 245 goto done; 246 } 247 248 /* 249 * Loop through the variables and dump them to uap->buf 250 */ 251 i = 0; 252 bytes = 0; 253 earlyterm = 0; 254 255 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 256 varsym_t sym = ve->ve_sym; 257 int namelen = strlen(sym->vs_name); 258 int datalen = strlen(sym->vs_data); 259 int totlen = namelen + datalen + 2; 260 261 /* 262 * Skip to our index point 263 */ 264 if (i < marker) { 265 ++i; 266 continue; 267 } 268 269 /* 270 * Stop if there is insufficient space in the user buffer. 271 * If we haven't stored anything yet return EOVERFLOW. 272 * Note that the marker index (i) does not change. 273 */ 274 if (bytes + totlen > uap->maxsize) { 275 if (bytes == 0) 276 error = EOVERFLOW; 277 earlyterm = 1; 278 break; 279 } 280 281 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1); 282 if (error == 0) { 283 bytes += namelen + 1; 284 error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1); 285 if (error == 0) 286 bytes += datalen + 1; 287 else 288 bytes -= namelen + 1; /* revert if error */ 289 } 290 if (error) { 291 earlyterm = 1; 292 break; 293 } 294 ++i; 295 } 296 297 /* 298 * Save the marker back. If no error occured and earlyterm is clear 299 * the marker is set to -1 indicating that the variable list has been 300 * exhausted. If no error occured the number of bytes loaded into 301 * the buffer will be returned, otherwise the syscall code returns -1. 302 */ 303 if (error == 0 && earlyterm == 0) 304 marker = -1; 305 else 306 marker = i; 307 if (error == 0) 308 error = copyout(&marker, uap->marker, sizeof(marker)); 309 uap->sysmsg_result = bytes; 310 done: 311 return(error); 312 } 313 314 /* 315 * Lookup a variant symlink. XXX use a hash table. 316 */ 317 static 318 struct varsyment * 319 varsymlookup(struct varsymset *vss, const char *name, int namelen) 320 { 321 struct varsyment *ve; 322 323 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) { 324 varsym_t var = ve->ve_sym; 325 if (var->vs_namelen == namelen && 326 bcmp(name, var->vs_name, namelen) == 0 327 ) { 328 return(ve); 329 } 330 } 331 return(NULL); 332 } 333 334 varsym_t 335 varsymfind(int mask, const char *name, int namelen) 336 { 337 struct proc *p; 338 struct varsyment *ve = NULL; 339 varsym_t sym; 340 341 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) { 342 if (mask & VARSYM_PROC_MASK) 343 ve = varsymlookup(&p->p_varsymset, name, namelen); 344 if (ve == NULL && (mask & VARSYM_USER_MASK)) 345 ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen); 346 } 347 if (ve == NULL && (mask & VARSYM_SYS_MASK)) 348 ve = varsymlookup(&varsymset_sys, name, namelen); 349 if (ve) { 350 sym = ve->ve_sym; 351 ++sym->vs_refs; 352 return(sym); 353 } else { 354 return(NULL); 355 } 356 } 357 358 int 359 varsymmake(int level, const char *name, const char *data) 360 { 361 struct varsymset *vss = NULL; 362 struct varsyment *ve; 363 struct proc *p = curproc; 364 varsym_t sym; 365 int namelen = strlen(name); 366 int datalen; 367 int error; 368 369 switch(level) { 370 case VARSYM_PROC: 371 if (p) 372 vss = &p->p_varsymset; 373 break; 374 case VARSYM_USER: 375 if (p) 376 vss = &p->p_ucred->cr_uidinfo->ui_varsymset; 377 break; 378 case VARSYM_SYS: 379 vss = &varsymset_sys; 380 break; 381 } 382 if (vss == NULL) { 383 error = EINVAL; 384 } else if (data && vss->vx_setsize >= MAXVARSYM_SET) { 385 error = E2BIG; 386 } else if (data) { 387 datalen = strlen(data); 388 ve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 389 sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK); 390 ve->ve_sym = sym; 391 sym->vs_refs = 1; 392 sym->vs_namelen = namelen; 393 sym->vs_name = (char *)(sym + 1); 394 sym->vs_data = sym->vs_name + namelen + 1; 395 strcpy(sym->vs_name, name); 396 strcpy(sym->vs_data, data); 397 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry); 398 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8; 399 error = 0; 400 } else { 401 if ((ve = varsymlookup(vss, name, namelen)) != NULL) { 402 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 403 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8; 404 varsymdrop(ve->ve_sym); 405 free(ve, M_VARSYM); 406 error = 0; 407 } else { 408 error = ENOENT; 409 } 410 } 411 return(error); 412 } 413 414 void 415 varsymdrop(varsym_t sym) 416 { 417 KKASSERT(sym->vs_refs > 0); 418 if (--sym->vs_refs == 0) { 419 free(sym, M_VARSYM); 420 } 421 } 422 423 static void 424 varsymdup(struct varsymset *vss, struct varsyment *ve) 425 { 426 struct varsyment *nve; 427 428 nve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO); 429 nve->ve_sym = ve->ve_sym; 430 ++nve->ve_sym->vs_refs; 431 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry); 432 } 433 434 void 435 varsymset_init(struct varsymset *vss, struct varsymset *copy) 436 { 437 struct varsyment *ve; 438 439 TAILQ_INIT(&vss->vx_queue); 440 if (copy) { 441 TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) { 442 varsymdup(vss, ve); 443 } 444 vss->vx_setsize = copy->vx_setsize; 445 } 446 } 447 448 void 449 varsymset_clean(struct varsymset *vss) 450 { 451 struct varsyment *ve; 452 453 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) { 454 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry); 455 varsymdrop(ve->ve_sym); 456 free(ve, M_VARSYM); 457 } 458 vss->vx_setsize = 0; 459 } 460 461