Merge from vendor branch GDB:
[dragonfly.git] / sys / kern / kern_varsym.c
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, &copy->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