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/malloc.h>
39 #include <sys/spinlock2.h>
40 #include <sys/fcntl.h>
41 #include <sys/device.h>
42 #include <sys/mount.h>
43 #include <sys/devfs.h>
44 #include <sys/devfs_rules.h>
46 MALLOC_DECLARE(M_DEVFS
);
48 static d_open_t devfs_dev_open
;
49 static d_close_t devfs_dev_close
;
50 static d_ioctl_t devfs_dev_ioctl
;
52 static struct devfs_rule
*devfs_rule_alloc(struct devfs_rule_ioctl
*);
53 static void devfs_rule_free(struct devfs_rule
*);
54 static int devfs_rule_insert(struct devfs_rule_ioctl
*);
55 static void devfs_rule_remove(struct devfs_rule
*);
56 static int devfs_rule_clear(struct devfs_rule_ioctl
*);
57 static void devfs_rule_create_link(struct devfs_node
*, struct devfs_rule
*);
58 static int devfs_rule_checkname(struct devfs_rule
*, struct devfs_node
*);
60 static struct objcache
*devfs_rule_cache
;
61 static struct lock devfs_rule_lock
;
63 static struct objcache_malloc_args devfs_rule_malloc_args
= {
64 sizeof(struct devfs_rule
), M_DEVFS
};
66 static cdev_t devfs_dev
;
67 static struct devfs_rule_head devfs_rule_list
=
68 TAILQ_HEAD_INITIALIZER(devfs_rule_list
);
70 static struct dev_ops devfs_dev_ops
= {
71 { "devfs", 0, D_MPSAFE
},
72 .d_open
= devfs_dev_open
,
73 .d_close
= devfs_dev_close
,
74 .d_ioctl
= devfs_dev_ioctl
78 static struct devfs_rule
*
79 devfs_rule_alloc(struct devfs_rule_ioctl
*templ
)
81 struct devfs_rule
*rule
;
84 rule
= objcache_get(devfs_rule_cache
, M_WAITOK
);
85 memset(rule
, 0, sizeof(struct devfs_rule
));
87 if (templ
->mntpoint
== NULL
)
91 len
= strlen(templ
->mntpoint
);
96 rule
->mntpoint
= kstrdup(templ
->mntpoint
, M_DEVFS
);
97 rule
->mntpointlen
= len
;
99 if (templ
->rule_type
& DEVFS_RULE_NAME
) {
100 if (templ
->name
== NULL
)
104 len
= strlen(templ
->name
);
109 rule
->name
= kstrdup(templ
->name
, M_DEVFS
);
113 if (templ
->rule_cmd
& DEVFS_RULE_LINK
) {
114 if (templ
->linkname
== NULL
)
118 len
= strlen(templ
->linkname
);
123 rule
->linkname
= kstrdup(templ
->linkname
, M_DEVFS
);
124 rule
->linknamlen
= len
;
127 rule
->rule_type
= templ
->rule_type
;
128 rule
->rule_cmd
= templ
->rule_cmd
;
129 rule
->dev_type
= templ
->dev_type
;
130 rule
->mode
= templ
->mode
;
131 rule
->uid
= templ
->uid
;
132 rule
->gid
= templ
->gid
;
137 devfs_rule_free(rule
);
143 devfs_rule_free(struct devfs_rule
*rule
)
145 if (rule
->mntpoint
!= NULL
) {
146 kfree(rule
->mntpoint
, M_DEVFS
);
149 if (rule
->name
!= NULL
) {
150 kfree(rule
->name
, M_DEVFS
);
153 if (rule
->linkname
!= NULL
) {
154 kfree(rule
->linkname
, M_DEVFS
);
156 objcache_put(devfs_rule_cache
, rule
);
161 devfs_rule_insert(struct devfs_rule_ioctl
*templ
)
163 struct devfs_rule
*rule
;
165 rule
= devfs_rule_alloc(templ
);
169 lockmgr(&devfs_rule_lock
, LK_EXCLUSIVE
);
170 TAILQ_INSERT_TAIL(&devfs_rule_list
, rule
, link
);
171 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
178 devfs_rule_remove(struct devfs_rule
*rule
)
180 TAILQ_REMOVE(&devfs_rule_list
, rule
, link
);
181 devfs_rule_free(rule
);
186 devfs_rule_clear(struct devfs_rule_ioctl
*templ
)
188 struct devfs_rule
*rule1
, *rule2
;
191 if (templ
->mntpoint
== NULL
)
194 mntpointlen
= strlen(templ
->mntpoint
);
195 if (mntpointlen
== 0)
198 lockmgr(&devfs_rule_lock
, LK_EXCLUSIVE
);
199 TAILQ_FOREACH_MUTABLE(rule1
, &devfs_rule_list
, link
, rule2
) {
200 if ((templ
->mntpoint
[0] == '*') ||
201 ((mntpointlen
== rule1
->mntpointlen
) &&
202 (!memcmp(templ
->mntpoint
, rule1
->mntpoint
, mntpointlen
)))) {
203 devfs_rule_remove(rule1
);
206 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
213 devfs_rule_reset_node(struct devfs_node
*node
, void *unused
)
216 * Don't blindly unhide all devices, some, like unix98 pty masters,
217 * haven't been hidden by a rule.
219 if (node
->flags
& DEVFS_RULE_HIDDEN
)
220 node
->flags
&= ~(DEVFS_HIDDEN
| DEVFS_RULE_HIDDEN
);
222 if ((node
->node_type
== Nlink
) && (node
->flags
& DEVFS_RULE_CREATED
)) {
223 KKASSERT(node
->link_target
);
224 node
->flags
&= ~DEVFS_RULE_CREATED
;
225 --node
->link_target
->nlinks
;
227 } else if ((node
->node_type
== Ndev
) && (node
->d_dev
)) {
228 node
->uid
= node
->d_dev
->si_uid
;
229 node
->gid
= node
->d_dev
->si_gid
;
230 node
->mode
= node
->d_dev
->si_perms
;
237 devfs_rule_create_link(struct devfs_node
*node
, struct devfs_rule
*rule
)
241 char *name
, name_buf
[PATH_MAX
], buf
[PATH_MAX
];
243 if (rule
->name
[rule
->namlen
-1] == '*') {
244 devfs_resolve_name_path(rule
->name
, name_buf
, &path
, &name
);
247 ksnprintf(buf
, sizeof(buf
), "%s%s",
248 rule
->linkname
, node
->d_dir
.d_name
+len
);
249 devfs_alias_create(buf
, node
, 1);
251 devfs_alias_create(rule
->linkname
, node
, 1);
256 devfs_rule_check_apply(struct devfs_node
*node
, void *unused
)
258 struct devfs_rule
*rule
;
259 struct mount
*mp
= node
->mp
;
262 /* Check if it is locked already. if not, we acquire the devfs lock */
263 if ((lockstatus(&devfs_rule_lock
, curthread
)) != LK_EXCLUSIVE
) {
264 lockmgr(&devfs_rule_lock
, LK_EXCLUSIVE
);
268 TAILQ_FOREACH(rule
, &devfs_rule_list
, link
) {
270 * Skip this rule if it is only intended for jailed mount points
271 * and the current mount point isn't jailed
273 if ((rule
->rule_type
& DEVFS_RULE_JAIL
) &&
274 (!(DEVFS_MNTDATA(mp
)->jailed
)) )
278 * Skip this rule if it is not intended for jailed mount points
279 * and the current mount point is jailed.
281 if (!(rule
->rule_type
& DEVFS_RULE_JAIL
) &&
282 (DEVFS_MNTDATA(mp
)->jailed
))
286 * Skip this rule if the mount point specified in the rule doesn't
287 * match the mount point of the node
289 if ((rule
->mntpoint
[0] != '*') &&
290 (strcmp(rule
->mntpoint
, mp
->mnt_stat
.f_mntonname
)))
294 * Skip this rule if this is a by-type rule and the device flags
295 * don't match the specified device type in the rule
297 if ((rule
->rule_type
& DEVFS_RULE_TYPE
) &&
298 ( (rule
->dev_type
== 0) || (!dev_is_good(node
->d_dev
)) ||
299 (!(dev_dflags(node
->d_dev
) & rule
->dev_type
))) )
303 * Skip this rule if this is a by-name rule and the node name
304 * doesn't match the wildcard string in the rule
306 if ((rule
->rule_type
& DEVFS_RULE_NAME
) &&
307 (!devfs_rule_checkname(rule
, node
)) )
310 if (rule
->rule_cmd
& DEVFS_RULE_HIDE
) {
312 * If we should hide the device, we just apply the relevant
313 * hide flag to the node and let devfs do the rest in the
316 if ((node
->d_dir
.d_namlen
== 5) &&
317 (!memcmp(node
->d_dir
.d_name
, "devfs", 5))) {
319 * Magically avoid /dev/devfs from being hidden, so that one
320 * can still use the rule system even after a "* hide".
324 node
->flags
|= (DEVFS_HIDDEN
| DEVFS_RULE_HIDDEN
);
325 } else if (rule
->rule_cmd
& DEVFS_RULE_SHOW
) {
327 * Show rule just means that the node should not be hidden, so
328 * what we do is clear the hide flag from the node.
330 node
->flags
&= ~DEVFS_HIDDEN
;
331 } else if (rule
->rule_cmd
& DEVFS_RULE_LINK
) {
333 * This is a LINK rule, so we tell devfs to create
334 * a link with the correct name to this node.
336 devfs_rule_create_link(node
, rule
);
338 devfs_alias_create(rule
->linkname
, node
, 1);
340 } else if (rule
->rule_cmd
& DEVFS_RULE_PERM
) {
342 * This is a normal ownership/permission rule. We
343 * just apply the permissions and ownership and
346 node
->mode
= rule
->mode
;
347 node
->uid
= rule
->uid
;
348 node
->gid
= rule
->gid
;
352 /* If we acquired the lock, we also get rid of it */
354 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
361 devfs_rule_checkname(struct devfs_rule
*rule
, struct devfs_node
*node
)
363 struct devfs_node
*parent
= DEVFS_MNTDATA(node
->mp
)->root_node
;
365 char *name
, name_buf
[PATH_MAX
];
368 devfs_resolve_name_path(rule
->name
, name_buf
, &path
, &name
);
369 parent
= devfs_resolve_or_create_path(parent
, path
, 0);
372 return 0; /* no match */
374 /* Check if node is a child of the parent we found */
375 if (node
->parent
!= parent
)
376 return 0; /* no match */
379 if (rule
->rule_type
& DEVFS_RULE_LINK
)
380 no_match
= memcmp(name
, node
->d_dir
.d_name
, strlen(name
));
383 no_match
= devfs_WildCaseCmp(name
, node
->d_dir
.d_name
);
390 devfs_dev_open(struct dev_open_args
*ap
)
393 * Only allow read-write access.
395 if (((ap
->a_oflags
& FWRITE
) == 0) || ((ap
->a_oflags
& FREAD
) == 0))
399 * We don't allow nonblocking access.
401 if ((ap
->a_oflags
& O_NONBLOCK
) != 0) {
402 devfs_debug(DEVFS_DEBUG_SHOW
,
403 "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
, 0, UID_ROOT
, GID_WHEEL
,
476 devfs_dev_uninit(void *unused
)
478 /* XXX: destroy all rules first */
479 destroy_dev(devfs_dev
);
480 objcache_destroy(devfs_rule_cache
);
484 SYSINIT(devfsdev
, SI_SUB_DRIVERS
, SI_ORDER_FIRST
, devfs_dev_init
, NULL
);
485 SYSUNINIT(devfsdev
, SI_SUB_DRIVERS
, SI_ORDER_FIRST
, devfs_dev_uninit
, NULL
);