2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
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>
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>
47 MALLOC_DECLARE(M_DEVFS);
49 static d_open_t devfs_dev_open;
50 static d_close_t devfs_dev_close;
51 static d_ioctl_t devfs_dev_ioctl;
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 *);
61 static struct objcache *devfs_rule_cache;
62 static struct lock devfs_rule_lock;
64 static struct objcache_malloc_args devfs_rule_malloc_args = {
65 sizeof(struct devfs_rule), M_DEVFS };
67 static cdev_t devfs_dev;
68 static struct devfs_rule_head devfs_rule_list =
69 TAILQ_HEAD_INITIALIZER(devfs_rule_list);
71 static struct dev_ops devfs_dev_ops = {
73 .d_open = devfs_dev_open,
74 .d_close = devfs_dev_close,
75 .d_ioctl = devfs_dev_ioctl
79 static struct devfs_rule *
80 devfs_rule_alloc(struct devfs_rule_ioctl *templ)
82 struct devfs_rule *rule;
85 rule = objcache_get(devfs_rule_cache, M_WAITOK);
86 memset(rule, 0, sizeof(struct devfs_rule));
88 if (templ->mntpoint == NULL)
92 len = strlen(templ->mntpoint);
97 rule->mntpoint = kstrdup(templ->mntpoint, M_DEVFS);
98 rule->mntpointlen = len;
100 if (templ->rule_type & DEVFS_RULE_NAME) {
101 if (templ->name == NULL)
105 len = strlen(templ->name);
110 rule->name = kstrdup(templ->name, M_DEVFS);
114 if (templ->rule_cmd & DEVFS_RULE_LINK) {
115 if (templ->linkname == NULL)
119 len = strlen(templ->linkname);
124 rule->linkname = kstrdup(templ->linkname, M_DEVFS);
125 rule->linknamlen = len;
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;
138 devfs_rule_free(rule);
144 devfs_rule_free(struct devfs_rule *rule)
146 if (rule->mntpoint != NULL) {
147 kfree(rule->mntpoint, M_DEVFS);
150 if (rule->name != NULL) {
151 kfree(rule->name, M_DEVFS);
154 if (rule->linkname != NULL) {
155 kfree(rule->linkname, M_DEVFS);
157 objcache_put(devfs_rule_cache, rule);
162 devfs_rule_insert(struct devfs_rule_ioctl *templ)
164 struct devfs_rule *rule;
166 rule = devfs_rule_alloc(templ);
170 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
171 TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
172 lockmgr(&devfs_rule_lock, LK_RELEASE);
179 devfs_rule_remove(struct devfs_rule *rule)
181 TAILQ_REMOVE(&devfs_rule_list, rule, link);
182 devfs_rule_free(rule);
187 devfs_rule_clear(struct devfs_rule_ioctl *templ)
189 struct devfs_rule *rule1, *rule2;
192 if (templ->mntpoint == NULL)
195 mntpointlen = strlen(templ->mntpoint);
196 if (mntpointlen == 0)
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);
207 lockmgr(&devfs_rule_lock, LK_RELEASE);
214 devfs_rule_reset_node(struct devfs_node *node, void *unused)
217 * Don't blindly unhide all devices, some, like unix98 pty masters,
218 * haven't been hidden by a rule.
220 if (node->flags & DEVFS_RULE_HIDDEN)
221 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
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;
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;
238 devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule)
242 char *name, name_buf[PATH_MAX], buf[PATH_MAX];
244 if (rule->name[rule->namlen-1] == '*') {
245 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
248 ksnprintf(buf, sizeof(buf), "%s%s",
249 rule->linkname, node->d_dir.d_name+len);
250 devfs_alias_create(buf, node, 1);
252 devfs_alias_create(rule->linkname, node, 1);
257 devfs_rule_check_apply(struct devfs_node *node, void *unused)
259 struct devfs_rule *rule;
260 struct mount *mp = node->mp;
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);
269 TAILQ_FOREACH(rule, &devfs_rule_list, link) {
271 * Skip this rule if it is only intended for jailed mount points
272 * and the current mount point isn't jailed
274 if ((rule->rule_type & DEVFS_RULE_JAIL) &&
275 (!(DEVFS_MNTDATA(mp)->jailed)) )
279 * Skip this rule if it is not intended for jailed mount points
280 * and the current mount point is jailed.
282 if (!(rule->rule_type & DEVFS_RULE_JAIL) &&
283 (DEVFS_MNTDATA(mp)->jailed))
287 * Skip this rule if the mount point specified in the rule doesn't
288 * match the mount point of the node
290 if ((rule->mntpoint[0] != '*') &&
291 (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname)))
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
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))) )
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
307 if ((rule->rule_type & DEVFS_RULE_NAME) &&
308 (!devfs_rule_checkname(rule, node)) )
311 if (rule->rule_cmd & DEVFS_RULE_HIDE) {
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
317 if ((node->d_dir.d_namlen == 5) &&
318 (!memcmp(node->d_dir.d_name, "devfs", 5))) {
320 * Magically avoid /dev/devfs from being hidden, so that one
321 * can still use the rule system even after a "* hide".
325 node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
326 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) {
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.
331 node->flags &= ~DEVFS_HIDDEN;
332 } else if (rule->rule_cmd & DEVFS_RULE_LINK) {
334 * This is a LINK rule, so we tell devfs to create
335 * a link with the correct name to this node.
337 devfs_rule_create_link(node, rule);
339 devfs_alias_create(rule->linkname, node, 1);
341 } else if (rule->rule_cmd & DEVFS_RULE_PERM) {
343 * This is a normal ownership/permission rule. We
344 * just apply the permissions and ownership and
347 node->mode = rule->mode;
348 node->uid = rule->uid;
349 node->gid = rule->gid;
353 /* If we acquired the lock, we also get rid of it */
355 lockmgr(&devfs_rule_lock, LK_RELEASE);
362 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
364 struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
366 char *name, name_buf[PATH_MAX];
369 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
370 parent = devfs_resolve_or_create_path(parent, path, 0);
373 return 0; /* no match */
375 /* Check if node is a child of the parent we found */
376 if (node->parent != parent)
377 return 0; /* no match */
380 if (rule->rule_type & DEVFS_RULE_LINK)
381 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
384 no_match = devfs_WildCaseCmp(name, node->d_dir.d_name);
391 devfs_dev_open(struct dev_open_args *ap)
394 * Only allow read-write access.
396 if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
400 * We don't allow nonblocking access.
402 if ((ap->a_oflags & O_NONBLOCK) != 0) {
403 devfs_debug(DEVFS_DEBUG_SHOW, "devfs_dev: can't do nonblocking access\n");
412 devfs_dev_close(struct dev_close_args *ap)
419 devfs_dev_ioctl(struct dev_ioctl_args *ap)
422 struct devfs_rule_ioctl *rule;
425 rule = (struct devfs_rule_ioctl *)ap->a_data;
429 error = devfs_rule_insert(rule);
432 case DEVFS_RULE_APPLY:
433 if (rule->mntpoint == NULL)
436 devfs_apply_rules(rule->mntpoint);
439 case DEVFS_RULE_CLEAR:
440 error = devfs_rule_clear(rule);
443 case DEVFS_RULE_RESET:
444 if (rule->mntpoint == NULL)
447 devfs_reset_rules(rule->mntpoint);
451 error = ENOTTY; /* Inappropriate ioctl for device */
460 devfs_dev_init(void *unused)
462 lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0);
464 devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0,
466 objcache_malloc_alloc,
467 objcache_malloc_free,
468 &devfs_rule_malloc_args );
470 devfs_dev = make_dev(&devfs_dev_ops,
480 devfs_dev_uninit(void *unused)
482 /* XXX: destroy all rules first */
483 destroy_dev(devfs_dev);
484 objcache_destroy(devfs_rule_cache);
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);