xref: /dflybsd-src/sys/vfs/devfs/devfs_rules.c (revision 3e82b46c18bc48fdb3c1d60729c7661b3a0bf6bf)
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.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 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/ioccom.h>
40 #include <sys/fcntl.h>
41 #include <sys/device.h>
42 #include <sys/mount.h>
43 #include <vfs/devfs/devfs.h>
44 #include <vfs/devfs/devfs_rules.h>
45 
46 MALLOC_DECLARE(M_DEVFS);
47 
48 
49 static int WildCmp(const char *w, const char *s);
50 static int WildCaseCmp(const char *w, const char *s);
51 static int wildCmp(const char **mary, int d, const char *w, const char *s);
52 static int wildCaseCmp(const char **mary, int d, const char *w, const char *s);
53 
54 static d_open_t      devfs_dev_open;
55 static d_close_t     devfs_dev_close;
56 static d_ioctl_t     devfs_dev_ioctl;
57 
58 static struct devfs_rule *devfs_rule_alloc(struct devfs_rule *);
59 static void devfs_rule_free(struct devfs_rule *);
60 static void devfs_rule_insert(struct devfs_rule *);
61 static void devfs_rule_remove(struct devfs_rule *);
62 static void devfs_rule_clear(struct devfs_rule *);
63 
64 static int devfs_rule_checkname(struct devfs_rule *, struct devfs_node *);
65 
66 static struct objcache	*devfs_rule_cache;
67 static struct lock 		devfs_rule_lock;
68 
69 static struct objcache_malloc_args devfs_rule_malloc_args = {
70 	sizeof(struct devfs_rule), M_DEVFS };
71 
72 static cdev_t devfs_dev;
73 static struct devfs_rule_head devfs_rule_list = TAILQ_HEAD_INITIALIZER(devfs_rule_list);
74 
75 static struct dev_ops devfs_dev_ops = {
76 	{ "devfs", 0, 0 },
77 	.d_open = devfs_dev_open,
78 	.d_close = devfs_dev_close,
79 	.d_ioctl = devfs_dev_ioctl
80 };
81 
82 
83 static struct devfs_rule *
84 devfs_rule_alloc(struct devfs_rule *templ)
85 {
86 	struct devfs_rule *rule = objcache_get(devfs_rule_cache, M_WAITOK);
87 
88 	memcpy(rule, templ, sizeof(struct devfs_rule));
89 	return rule;
90 }
91 
92 
93 static void
94 devfs_rule_free(struct devfs_rule *rule)
95 {
96 	objcache_put(devfs_rule_cache, rule);
97 }
98 
99 
100 static void
101 devfs_rule_insert(struct devfs_rule *templ)
102 {
103 	struct devfs_rule *rule = devfs_rule_alloc(templ);
104 	lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
105 	rule->mntpointlen = strlen(rule->mntpoint);
106 	TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
107 	lockmgr(&devfs_rule_lock, LK_RELEASE);
108 }
109 
110 
111 static void
112 devfs_rule_remove(struct devfs_rule *rule)
113 {
114 	TAILQ_REMOVE(&devfs_rule_list, rule, link);
115 	devfs_rule_free(rule);
116 }
117 
118 
119 static void
120 devfs_rule_clear(struct devfs_rule *rule)
121 {
122 	struct devfs_rule *rule1, *rule2;
123 	rule->mntpointlen = strlen(rule->mntpoint);
124 
125 	lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
126 	TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) {
127 		if ((rule->mntpoint[0] == '*') ||
128 			( (rule->mntpointlen == rule1->mntpointlen) &&
129 			  (!memcmp(rule->mntpoint, rule1->mntpoint, rule->mntpointlen)) )) {
130 			devfs_rule_remove(rule1);
131 		}
132 	}
133 	lockmgr(&devfs_rule_lock, LK_RELEASE);
134 }
135 
136 
137 int
138 devfs_rule_reset_node(struct devfs_node *node)
139 {
140 	node->flags &= ~DEVFS_HIDDEN;
141 
142 	if ((node->node_type == Pdev) && (node->d_dev)) {
143 		node->uid = node->d_dev->si_uid;
144 		node->gid = node->d_dev->si_gid;
145 		node->mode = node->d_dev->si_perms;
146 	}
147 
148 	return 0;
149 }
150 
151 
152 int
153 devfs_rule_check_apply(struct devfs_node *node)
154 {
155 	struct devfs_rule *rule;
156 	struct mount *mp = node->mp;
157 	int applies = 0;
158 
159 	lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
160 	TAILQ_FOREACH(rule, &devfs_rule_list, link) {
161 
162 		/*
163 		 * Skip this rule if it is only intended for jailed mount points
164 		 * and the current mount point isn't jailed
165 		 */
166 		if ((rule->rule_type & DEVFS_RULE_JAIL) &&
167 			(!(DEVFS_MNTDATA(mp)->jailed)) )
168 			continue;
169 
170 		/*
171 		 * Skip this rule if the mount point specified in the rule doesn't
172 		 * match the mount point of the node
173 		 */
174 		if ((rule->mntpoint[0] != '*') &&
175 			((rule->mntpointlen != DEVFS_MNTDATA(mp)->mntonnamelen) ||
176 			(memcmp(rule->mntpoint, mp->mnt_stat.f_mntonname, rule->mntpointlen))))
177 			continue;
178 
179 		/*
180 		 * Skip this rule if this is a by-type rule and the device flags
181 		 * don't match the specified device type in the rule
182 		 */
183 		if ((rule->rule_type & DEVFS_RULE_TYPE) &&
184 			( (rule->dev_type == 0) || (!dev_is_good(node->d_dev)) ||
185 			  (!dev_dflags(node->d_dev) & rule->dev_type)) )
186 			continue;
187 
188 		/*
189 		 * Skip this rule if this is a by-name rule and the node name
190 		 * doesn't match the wildcard string in the rule
191 		 */
192 		if ((rule->rule_type & DEVFS_RULE_NAME) &&
193 			(!devfs_rule_checkname(rule, node)) )
194 			continue;
195 
196 
197 		if (rule->rule_type & DEVFS_RULE_HIDE) {
198 			/*
199 			 * If we should hide the device, we just apply the relevant
200 			 * hide flag to the node and let devfs do the rest in the
201 			 * vnops
202 			 */
203 			node->flags |= DEVFS_HIDDEN;
204 			applies = 1;
205 		} else if (rule->rule_type & DEVFS_RULE_SHOW) {
206 			/*
207 			 * Show rule just means that the node should not be hidden, so
208 			 * what we do is clear the hide flag from the node.
209 			 */
210 			node->flags &= ~DEVFS_HIDDEN;
211 			applies = 1;
212 		} else if ((rule->rule_type & DEVFS_RULE_LINK) && (node->node_type != Plink)) {
213 			/*
214 			 * This is a LINK rule, so we tell devfs to create
215 			 * a link with the correct name to this node.
216 			 */
217 			devfs_alias_create(rule->linkname, node);
218 			applies = 1;
219 		} else {
220 			/*
221 			 * This is a normal ownership/permission rule. We
222 			 * just apply the permissions and ownership and
223 			 * we are done.
224 			 */
225 			node->mode = rule->mode;
226 			node->uid = rule->uid;
227 			node->gid = rule->gid;
228 			applies = 1;
229 		}
230 	}
231 	lockmgr(&devfs_rule_lock, LK_RELEASE);
232 	return applies;
233 }
234 
235 
236 static int
237 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
238 {
239 	struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
240 	char *path = NULL;
241 	char *name, name_buf[PATH_MAX];
242 	int no_match = 0;
243 
244 	devfs_resolve_name_path(rule->name, name_buf, &path, &name);
245 	parent = devfs_resolve_or_create_path(parent, path, 0);
246 
247 	if (parent == NULL)
248 		return 0; /* no match */
249 
250 	/* Check if node is a child of the parent we found */
251 	if (node->parent != parent)
252 		return 0; /* no match */
253 
254 	if (rule->rule_type & DEVFS_RULE_LINK)
255 		no_match = memcmp(name, node->d_dir.d_name, strlen(name));
256 	else
257 		no_match = WildCaseCmp(name, node->d_dir.d_name);
258 
259 	return !no_match;
260 }
261 
262 
263 static int
264 devfs_dev_open(struct dev_open_args *ap)
265 {
266 	/*
267 	 * Only allow read-write access.
268 	 */
269 	if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
270 		return(EPERM);
271 
272 	/*
273 	 * We don't allow nonblocking access.
274 	 */
275 	if ((ap->a_oflags & O_NONBLOCK) != 0) {
276 		devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_dev: can't do nonblocking access\n");
277 		return(ENODEV);
278 	}
279 
280 	return 0;
281 }
282 
283 
284 static int
285 devfs_dev_close(struct dev_close_args *ap)
286 {
287 	return 0;
288 }
289 
290 
291 static int
292 devfs_dev_ioctl(struct dev_ioctl_args *ap)
293 {
294 	int error;
295 	struct devfs_rule *rule;
296 
297 	error = 0;
298 	rule = (struct devfs_rule *)ap->a_data;
299 
300 	switch(ap->a_cmd) {
301 	case DEVFS_RULE_ADD:
302 		devfs_rule_insert(rule);
303 		break;
304 
305 	case DEVFS_RULE_APPLY:
306 		devfs_apply_rules(rule->mntpoint);
307 		break;
308 
309 	case DEVFS_RULE_CLEAR:
310 		devfs_rule_clear(rule);
311 		break;
312 
313 	case DEVFS_RULE_RESET:
314 		devfs_reset_rules(rule->mntpoint);
315 		break;
316 
317 	default:
318 		error = ENOTTY; /* Inappropriate ioctl for device */
319 		break;
320 	}
321 
322 	return(error);
323 }
324 
325 
326 static void
327 devfs_dev_init(void *unused)
328 {
329 	lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0);
330 
331     devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0,
332 			NULL, NULL, NULL,
333 			objcache_malloc_alloc,
334 			objcache_malloc_free,
335 			&devfs_rule_malloc_args );
336 
337     devfs_dev = make_dev(&devfs_dev_ops,
338             0,
339             UID_ROOT,
340             GID_WHEEL,
341             0600,
342             "devfs");
343 }
344 
345 
346 static void
347 devfs_dev_uninit(void *unused)
348 {
349 	//XXX: destroy all rules first
350     destroy_dev(devfs_dev);
351 	objcache_destroy(devfs_rule_cache);
352 }
353 
354 
355 SYSINIT(devfsdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_init,NULL)
356 SYSUNINIT(devfsdev, SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_uninit, NULL);
357 
358 
359 
360 static int
361 WildCmp(const char *w, const char *s)
362 {
363     int i;
364     int c;
365     int slen = strlen(s);
366     const char **mary;
367 
368     for (i = c = 0; w[i]; ++i) {
369 	if (w[i] == '*')
370 	    ++c;
371     }
372     mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
373     for (i = 0; i < c; ++i)
374 	mary[i] = s + slen;
375     i = wildCmp(mary, 0, w, s);
376     kfree(mary, M_DEVFS);
377     return(i);
378 }
379 
380 static int
381 WildCaseCmp(const char *w, const char *s)
382 {
383     int i;
384     int c;
385     int slen = strlen(s);
386     const char **mary;
387 
388     for (i = c = 0; w[i]; ++i) {
389 	if (w[i] == '*')
390 	    ++c;
391     }
392     mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
393     for (i = 0; i < c; ++i)
394 	mary[i] = s + slen;
395     i = wildCaseCmp(mary, 0, w, s);
396     kfree(mary, M_DEVFS);
397     return(i);
398 }
399 
400 /*
401  * WildCmp() - compare wild string to sane string
402  *
403  *	Returns 0 on success, -1 on failure.
404  */
405 static int
406 wildCmp(const char **mary, int d, const char *w, const char *s)
407 {
408     int i;
409 
410     /*
411      * skip fixed portion
412      */
413     for (;;) {
414 	switch(*w) {
415 	case '*':
416 	    /*
417 	     * optimize terminator
418 	     */
419 	    if (w[1] == 0)
420 		return(0);
421 	    if (w[1] != '?' && w[1] != '*') {
422 		/*
423 		 * optimize * followed by non-wild
424 		 */
425 		for (i = 0; s + i < mary[d]; ++i) {
426 		    if (s[i] == w[1] && wildCmp(mary, d + 1, w + 1, s + i) == 0)
427 			return(0);
428 		}
429 	    } else {
430 		/*
431 		 * less-optimal
432 		 */
433 		for (i = 0; s + i < mary[d]; ++i) {
434 		    if (wildCmp(mary, d + 1, w + 1, s + i) == 0)
435 			return(0);
436 		}
437 	    }
438 	    mary[d] = s;
439 	    return(-1);
440 	case '?':
441 	    if (*s == 0)
442 		return(-1);
443 	    ++w;
444 	    ++s;
445 	    break;
446 	default:
447 	    if (*w != *s)
448 		return(-1);
449 	    if (*w == 0)	/* terminator */
450 		return(0);
451 	    ++w;
452 	    ++s;
453 	    break;
454 	}
455     }
456     /* not reached */
457     return(-1);
458 }
459 
460 
461 /*
462  * WildCaseCmp() - compare wild string to sane string, case insensitive
463  *
464  *	Returns 0 on success, -1 on failure.
465  */
466 static int
467 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
468 {
469     int i;
470 
471     /*
472      * skip fixed portion
473      */
474     for (;;) {
475 	switch(*w) {
476 	case '*':
477 	    /*
478 	     * optimize terminator
479 	     */
480 	    if (w[1] == 0)
481 		return(0);
482 	    if (w[1] != '?' && w[1] != '*') {
483 		/*
484 		 * optimize * followed by non-wild
485 		 */
486 		for (i = 0; s + i < mary[d]; ++i) {
487 		    if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
488 			return(0);
489 		}
490 	    } else {
491 		/*
492 		 * less-optimal
493 		 */
494 		for (i = 0; s + i < mary[d]; ++i) {
495 		    if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
496 			return(0);
497 		}
498 	    }
499 	    mary[d] = s;
500 	    return(-1);
501 	case '?':
502 	    if (*s == 0)
503 		return(-1);
504 	    ++w;
505 	    ++s;
506 	    break;
507 	default:
508 	    if (*w != *s) {
509 #define tolower(x)	((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x))
510 		if (tolower(*w) != tolower(*s))
511 		    return(-1);
512 	    }
513 	    if (*w == 0)	/* terminator */
514 		return(0);
515 	    ++w;
516 	    ++s;
517 	    break;
518 	}
519     }
520     /* not reached */
521     return(-1);
522 }
523