The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_varsym.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   35 /*
   36  * This module implements variable storage and management for variant
   37  * symlinks.  These variables may also be used for general purposes.
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/kernel.h>
   43 #include <sys/ucred.h>
   44 #include <sys/resourcevar.h>
   45 #include <sys/proc.h>
   46 #include <sys/priv.h>
   47 #include <sys/jail.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 #include <sys/mplock2.h>
   55 
   56 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
   57 
   58 struct varsymset        varsymset_sys;
   59 
   60 /*
   61  * Initialize the variant symlink subsystem
   62  */
   63 static void
   64 varsym_sysinit(void *dummy)
   65 {
   66     varsymset_init(&varsymset_sys, NULL);
   67 }
   68 SYSINIT(announce, SI_BOOT2_MACHDEP, SI_ORDER_FIRST, varsym_sysinit, NULL);
   69 
   70 /*
   71  * varsymreplace() - called from namei
   72  *
   73  *      Do variant symlink variable substitution
   74  */
   75 int
   76 varsymreplace(char *cp, int linklen, int maxlen)
   77 {
   78     int rlen;
   79     int xlen;
   80     int nlen;
   81     int i;
   82     varsym_t var;
   83 
   84     rlen = linklen;
   85     while (linklen > 1) {
   86         if (cp[0] == '$' && cp[1] == '{') {
   87             for (i = 2; i < linklen; ++i) {
   88                 if (cp[i] == '}')
   89                     break;
   90             }
   91             if (i < linklen && 
   92                 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
   93             ) {
   94                 xlen = i + 1;                   /* bytes to strike */
   95                 nlen = strlen(var->vs_data);    /* bytes to add */
   96                 if (linklen + nlen - xlen >= maxlen) {
   97                     varsymdrop(var);
   98                     return(-1);
   99                 }
  100                 KKASSERT(linklen >= xlen);
  101                 if (linklen != xlen)
  102                     bcopy(cp + xlen, cp + nlen, linklen - xlen);
  103                 bcopy(var->vs_data, cp, nlen);
  104                 linklen += nlen - xlen; /* new relative length */
  105                 rlen += nlen - xlen;    /* returned total length */
  106                 cp += nlen;             /* adjust past replacement */
  107                 linklen -= nlen;        /* adjust past replacement */
  108                 maxlen -= nlen;         /* adjust past replacement */
  109             } else {
  110                 /*
  111                  * It's ok if i points to the '}', it will simply be
  112                  * skipped.  i could also have hit linklen.
  113                  */
  114                 cp += i;
  115                 linklen -= i;
  116                 maxlen -= i;
  117             }
  118         } else {
  119             ++cp;
  120             --linklen;
  121             --maxlen;
  122         }
  123     }
  124     return(rlen);
  125 }
  126 
  127 /*
  128  * varsym_set() system call
  129  *
  130  * (int level, const char *name, const char *data)
  131  *
  132  * MPALMOSTSAFE
  133  */
  134 int
  135 sys_varsym_set(struct varsym_set_args *uap)
  136 {
  137     char name[MAXVARSYM_NAME];
  138     char *buf;
  139     struct thread *td;
  140     struct lwp *lp;
  141     int error;
  142 
  143     td = curthread;
  144     lp = td->td_lwp;
  145 
  146     if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
  147         goto done2;
  148     buf = kmalloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK);
  149     if (uap->data && 
  150         (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
  151     {
  152         goto done1;
  153     }
  154 
  155     get_mplock();
  156 
  157     switch(uap->level) {
  158     case VARSYM_SYS:
  159         if (lp != NULL && td->td_ucred->cr_prison != NULL)
  160             uap->level = VARSYM_PRISON;
  161     case VARSYM_PRISON:
  162         if (lp != NULL &&
  163             (error = priv_check_cred(td->td_ucred, PRIV_VARSYM_SYS, 0)) != 0)
  164             break;
  165         /* fall through */
  166     case VARSYM_USER:
  167         /* XXX check jail / implement per-jail user */
  168         /* fall through */
  169     case VARSYM_PROC:
  170         if (uap->data) {
  171             (void)varsymmake(uap->level, name, NULL);
  172             error = varsymmake(uap->level, name, buf);
  173         } else {
  174             error = varsymmake(uap->level, name, NULL);
  175         }
  176         break;
  177     }
  178     rel_mplock();
  179 done1:
  180     kfree(buf, M_TEMP);
  181 done2:
  182     return(error);
  183 }
  184 
  185 /*
  186  * varsym_get() system call
  187  *
  188  * (int mask, const char *wild, char *buf, int bufsize)
  189  *
  190  * MPALMOSTSAFE
  191  */
  192 int
  193 sys_varsym_get(struct varsym_get_args *uap)
  194 {
  195     char wild[MAXVARSYM_NAME];
  196     varsym_t sym;
  197     int error;
  198     int dlen;
  199 
  200     get_mplock();
  201     if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
  202         goto done;
  203     sym = varsymfind(uap->mask, wild, strlen(wild));
  204     if (sym == NULL) {
  205         error = ENOENT;
  206         goto done;
  207     }
  208     dlen = strlen(sym->vs_data);
  209     if (dlen < uap->bufsize) {
  210         copyout(sym->vs_data, uap->buf, dlen + 1);
  211     } else if (uap->bufsize) {
  212         copyout("", uap->buf, 1);
  213     }
  214     uap->sysmsg_result = dlen + 1;
  215     varsymdrop(sym);
  216 done:
  217     rel_mplock();
  218     return(error);
  219 }
  220 
  221 /*
  222  * varsym_list() system call
  223  *
  224  * (int level, char *buf, int maxsize, int *marker)
  225  *
  226  * MPALMOSTSAFE
  227  */
  228 int
  229 sys_varsym_list(struct varsym_list_args *uap)
  230 {
  231         struct varsymset *vss;
  232         struct varsyment *ve;
  233         struct thread *td;
  234         struct proc *p;
  235         struct lwp *lp;
  236         int i;
  237         int error;
  238         int bytes;
  239         int earlyterm;
  240         int marker;
  241 
  242         /*
  243          * Get the marker from userspace.
  244          */
  245         get_mplock();
  246         if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0)
  247                 goto done;
  248 
  249         /*
  250          * Figure out the varsym set.
  251          */
  252         td = curthread;
  253         lp = td->td_lwp;
  254         p = lp ? lp->lwp_proc : NULL;
  255 
  256         vss = NULL;
  257 
  258         switch (uap->level) {
  259         case VARSYM_PROC:
  260                 if (p)
  261                         vss = &p->p_varsymset;
  262                 break;
  263         case VARSYM_USER:
  264                 if (lp)
  265                         vss = &td->td_ucred->cr_uidinfo->ui_varsymset;
  266                 break;
  267         case VARSYM_SYS:
  268                 vss = &varsymset_sys;
  269                 break;
  270         case VARSYM_PRISON:
  271                 if (lp && td->td_ucred->cr_prison)
  272                         vss = &td->td_ucred->cr_prison->pr_varsymset;
  273                 break;
  274         }
  275         if (vss == NULL) {
  276                 error = EINVAL;
  277                 goto done;
  278         }
  279 
  280         /*
  281          * Loop through the variables and dump them to uap->buf
  282          */
  283         i = 0;
  284         bytes = 0;
  285         earlyterm = 0;
  286 
  287         lockmgr(&vss->vx_lock, LK_SHARED);
  288         TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
  289                 varsym_t sym = ve->ve_sym;
  290                 int namelen = strlen(sym->vs_name);
  291                 int datalen = strlen(sym->vs_data);
  292                 int totlen = namelen + datalen + 2;
  293 
  294                 /*
  295                  * Skip to our index point
  296                  */
  297                 if (i < marker) {
  298                         ++i;
  299                         continue;
  300                 }
  301 
  302                 /*
  303                  * Stop if there is insufficient space in the user buffer.
  304                  * If we haven't stored anything yet return EOVERFLOW. 
  305                  * Note that the marker index (i) does not change.
  306                  */
  307                 if (bytes + totlen > uap->maxsize) {
  308                         if (bytes == 0)
  309                                 error = EOVERFLOW;
  310                         earlyterm = 1;
  311                         break;
  312                 }
  313 
  314                 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1);
  315                 if (error == 0) {
  316                         bytes += namelen + 1;
  317                         error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1);
  318                         if (error == 0)
  319                                 bytes += datalen + 1;
  320                         else
  321                                 bytes -= namelen + 1;   /* revert if error */
  322                 }
  323                 if (error) {
  324                         earlyterm = 1;
  325                         break;
  326                 }
  327                 ++i;
  328         }
  329         lockmgr(&vss->vx_lock, LK_RELEASE);
  330 
  331         /*
  332          * Save the marker back.  If no error occured and earlyterm is clear
  333          * the marker is set to -1 indicating that the variable list has been
  334          * exhausted.  If no error occured the number of bytes loaded into
  335          * the buffer will be returned, otherwise the syscall code returns -1.
  336          */
  337         if (error == 0 && earlyterm == 0)
  338                 marker = -1;
  339         else
  340                 marker = i;
  341         if (error == 0)
  342                 error = copyout(&marker, uap->marker, sizeof(marker));
  343         uap->sysmsg_result = bytes;
  344 done:
  345         rel_mplock();
  346         return(error);
  347 }
  348 
  349 /*
  350  * Lookup a variant symlink.  XXX use a hash table.
  351  */
  352 static
  353 struct varsyment *
  354 varsymlookup(struct varsymset *vss, const char *name, int namelen)
  355 {
  356     struct varsyment *ve;
  357 
  358     KKASSERT(lockstatus(&vss->vx_lock, curthread) != 0);
  359     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
  360         varsym_t var = ve->ve_sym;
  361         if (var->vs_namelen == namelen && 
  362             bcmp(name, var->vs_name, namelen) == 0
  363         ) {
  364             return(ve);
  365         }
  366     }
  367     return(NULL);
  368 }
  369  
  370 static
  371 void
  372 vsslock(struct varsymset **vss, struct varsymset *n)
  373 {
  374         if (*vss) {
  375                 lockmgr(&(*vss)->vx_lock, LK_RELEASE);
  376         }
  377         lockmgr(&n->vx_lock, LK_SHARED);
  378         *vss = n;
  379 }
  380 
  381 varsym_t
  382 varsymfind(int mask, const char *name, int namelen)
  383 {
  384     struct varsyment *ve = NULL;
  385     struct varsymset *vss = NULL;
  386     struct thread *td;
  387     struct lwp *lp;
  388     struct proc *p;
  389     varsym_t sym;
  390 
  391     td = curthread;
  392     lp = td->td_lwp;
  393     p = lp ? lp->lwp_proc : NULL;
  394 
  395     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && lp != NULL) {
  396         if (mask & VARSYM_PROC_MASK) {
  397             vsslock(&vss, &p->p_varsymset);
  398             ve = varsymlookup(vss, name, namelen);
  399         }
  400         if (ve == NULL && (mask & VARSYM_USER_MASK)) {
  401             vsslock(&vss, &td->td_ucred->cr_uidinfo->ui_varsymset);
  402             ve = varsymlookup(vss, name, namelen);
  403         }
  404     }
  405     if (ve == NULL && (mask & VARSYM_SYS_MASK)) {
  406         if (lp != NULL && td->td_ucred->cr_prison) {
  407             vsslock(&vss, &td->td_ucred->cr_prison->pr_varsymset);
  408             ve = varsymlookup(vss, name, namelen);
  409         } else {
  410             vsslock(&vss, &varsymset_sys);
  411             ve = varsymlookup(vss, name, namelen);
  412         }
  413     }
  414     if (ve) {
  415         sym = ve->ve_sym;
  416         atomic_add_int(&sym->vs_refs, 1);
  417     } else {
  418         sym = NULL;
  419     }
  420     if (vss)
  421         lockmgr(&vss->vx_lock, LK_RELEASE);
  422     return sym;
  423 }
  424 
  425 int
  426 varsymmake(int level, const char *name, const char *data)
  427 {
  428     struct varsymset *vss = NULL;
  429     struct varsyment *ve;
  430     struct thread *td;
  431     struct proc *p;
  432     struct lwp *lp;
  433     varsym_t sym;
  434     int namelen = strlen(name);
  435     int datalen;
  436     int error;
  437 
  438     td = curthread;
  439     lp = td->td_lwp;
  440     p = lp ? lp->lwp_proc : NULL;
  441 
  442     switch(level) {
  443     case VARSYM_PROC:
  444         if (p)
  445             vss = &p->p_varsymset;
  446         break;
  447     case VARSYM_USER:
  448         if (lp)
  449             vss = &td->td_ucred->cr_uidinfo->ui_varsymset;
  450         break;
  451     case VARSYM_SYS:
  452         vss = &varsymset_sys;
  453         break;
  454     case VARSYM_PRISON:
  455         if (lp && td->td_ucred->cr_prison)
  456             vss = &td->td_ucred->cr_prison->pr_varsymset;
  457         break;
  458     }
  459     if (vss == NULL) {
  460         return EINVAL;
  461     }
  462     lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
  463     if (data && vss->vx_setsize >= MAXVARSYM_SET) {
  464         error = E2BIG;
  465     } else if (data) {
  466         datalen = strlen(data);
  467         ve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
  468         sym = kmalloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK);
  469         ve->ve_sym = sym;
  470         sym->vs_refs = 1;
  471         sym->vs_namelen = namelen;
  472         sym->vs_name = (char *)(sym + 1);
  473         sym->vs_data = sym->vs_name + namelen + 1;
  474         strcpy(sym->vs_name, name);
  475         strcpy(sym->vs_data, data);
  476         TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
  477         vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
  478         error = 0;
  479     } else {
  480         if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
  481             TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
  482             vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
  483             varsymdrop(ve->ve_sym);
  484             kfree(ve, M_VARSYM);
  485             error = 0;
  486         } else {
  487             error = ENOENT;
  488         }
  489     }
  490     lockmgr(&vss->vx_lock, LK_RELEASE);
  491     return(error);
  492 }
  493 
  494 void
  495 varsymdrop(varsym_t sym)
  496 {
  497     KKASSERT(sym->vs_refs > 0);
  498     if (atomic_fetchadd_int(&sym->vs_refs, -1) == 1) {
  499         kfree(sym, M_VARSYM);
  500     }
  501 }
  502 
  503 /*
  504  * Insert a duplicate of ve in vss. Does not do any locking,
  505  * so it is the callers responsibility to make sure nobody
  506  * else can mess with the TAILQ in vss at the same time.
  507  */
  508 static void
  509 varsymdup(struct varsymset *vss, struct varsyment *ve)
  510 {
  511     struct varsyment *nve;
  512 
  513     nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
  514     nve->ve_sym = ve->ve_sym;
  515     ++nve->ve_sym->vs_refs;     /* can't be reached, no need for atomic add */
  516     /*
  517      * We're only called through varsymset_init() so vss is not yet reachable,
  518      * no need to lock.
  519      */
  520     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
  521 }
  522 
  523 void
  524 varsymset_init(struct varsymset *vss, struct varsymset *copy)
  525 {
  526     struct varsyment *ve;
  527 
  528     TAILQ_INIT(&vss->vx_queue);
  529     lockinit(&vss->vx_lock, "vx", 0, 0);
  530     if (copy) {
  531         TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
  532             varsymdup(vss, ve);
  533         }
  534         vss->vx_setsize = copy->vx_setsize;
  535     }
  536 }
  537 
  538 void
  539 varsymset_clean(struct varsymset *vss)
  540 {
  541     struct varsyment *ve;
  542 
  543     lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
  544     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
  545         TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
  546         varsymdrop(ve->ve_sym);
  547         kfree(ve, M_VARSYM);
  548     }
  549     vss->vx_setsize = 0;
  550     lockmgr(&vss->vx_lock, LK_RELEASE);
  551 }
  552 

Cache object: d833b72cc293d88267a5418607a9d7f2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.