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