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