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