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