xref: /dragonfly/sys/kern/kern_varsym.c (revision 86fe9e07)
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 
461