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