xref: /dragonfly/sys/kern/kern_varsym.c (revision af79c6e5)
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, &copy->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