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>
39 #include <sys/ioccom.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>
48 MALLOC_DECLARE(M_DEVFS
);
51 static int WildCmp(const char *w
, const char *s
);
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
);
57 static d_open_t devfs_dev_open
;
58 static d_close_t devfs_dev_close
;
59 static d_ioctl_t devfs_dev_ioctl
;
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
*);
69 static struct objcache
*devfs_rule_cache
;
70 static struct lock devfs_rule_lock
;
72 static struct objcache_malloc_args devfs_rule_malloc_args
= {
73 sizeof(struct devfs_rule
), M_DEVFS
};
75 static cdev_t devfs_dev
;
76 static struct devfs_rule_head devfs_rule_list
=
77 TAILQ_HEAD_INITIALIZER(devfs_rule_list
);
79 static struct dev_ops devfs_dev_ops
= {
81 .d_open
= devfs_dev_open
,
82 .d_close
= devfs_dev_close
,
83 .d_ioctl
= devfs_dev_ioctl
87 static struct devfs_rule
*
88 devfs_rule_alloc(struct devfs_rule_ioctl
*templ
)
90 struct devfs_rule
*rule
;
93 rule
= objcache_get(devfs_rule_cache
, M_WAITOK
);
94 memset(rule
, 0, sizeof(struct devfs_rule
));
96 len
= strlen(templ
->mntpoint
);
98 rule
->mntpoint
= kstrdup(templ
->mntpoint
, M_DEVFS
);
99 rule
->mntpointlen
= len
;
102 if (templ
->rule_type
== DEVFS_RULE_NAME
) {
103 len
= strlen(templ
->name
);
105 rule
->name
= kstrdup(templ
->name
, M_DEVFS
);
110 if (templ
->rule_cmd
== DEVFS_RULE_LINK
) {
111 len
= strlen(templ
->linkname
);
113 rule
->linkname
= kstrdup(templ
->linkname
, M_DEVFS
);
114 rule
->linknamlen
= len
;
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
;
130 devfs_rule_free(struct devfs_rule
*rule
)
132 if (rule
->mntpoint
!= NULL
) {
133 kfree(rule
->mntpoint
, M_DEVFS
);
136 if (rule
->name
!= NULL
) {
137 kfree(rule
->name
, M_DEVFS
);
140 if (rule
->linkname
!= NULL
) {
141 kfree(rule
->linkname
, M_DEVFS
);
143 objcache_put(devfs_rule_cache
, rule
);
148 devfs_rule_insert(struct devfs_rule_ioctl
*templ
)
150 struct devfs_rule
*rule
;
152 rule
= devfs_rule_alloc(templ
);
154 lockmgr(&devfs_rule_lock
, LK_EXCLUSIVE
);
155 TAILQ_INSERT_TAIL(&devfs_rule_list
, rule
, link
);
156 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
161 devfs_rule_remove(struct devfs_rule
*rule
)
163 TAILQ_REMOVE(&devfs_rule_list
, rule
, link
);
164 devfs_rule_free(rule
);
169 devfs_rule_clear(struct devfs_rule_ioctl
*templ
)
171 struct devfs_rule
*rule1
, *rule2
;
172 size_t mntpointlen
= strlen(templ
->mntpoint
);
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
);
182 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
187 devfs_rule_reset_node(struct devfs_node
*node
, void *unused
)
190 * XXX: Don't blindly unhide all devices, some, like unix98 pty masters,
191 * haven't been hidden by a rule.
193 node
->flags
&= ~DEVFS_HIDDEN
;
195 if ((node
->node_type
== Plink
) && (node
->flags
& DEVFS_RULE_CREATED
)) {
196 KKASSERT(node
->link_target
);
197 node
->flags
&= ~DEVFS_RULE_CREATED
;
198 --node
->link_target
->nlinks
;
200 } else if ((node
->node_type
== Pdev
) && (node
->d_dev
)) {
201 node
->uid
= node
->d_dev
->si_uid
;
202 node
->gid
= node
->d_dev
->si_gid
;
203 node
->mode
= node
->d_dev
->si_perms
;
210 devfs_rule_create_link(struct devfs_node
*node
, struct devfs_rule
*rule
)
214 char *name
, name_buf
[PATH_MAX
], buf
[PATH_MAX
];
216 if (rule
->name
[rule
->namlen
-1] == '*') {
217 devfs_resolve_name_path(rule
->name
, name_buf
, &path
, &name
);
220 ksnprintf(buf
, sizeof(buf
), "%s%s",
221 rule
->linkname
, node
->d_dir
.d_name
+len
);
222 devfs_alias_create(buf
, node
, 1);
224 devfs_alias_create(rule
->linkname
, node
, 1);
229 devfs_rule_check_apply(struct devfs_node
*node
, void *unused
)
231 struct devfs_rule
*rule
;
232 struct mount
*mp
= node
->mp
;
236 /* Check if it is locked already. if not, we acquire the devfs lock */
237 if (!(lockstatus(&devfs_rule_lock
, curthread
)) == LK_EXCLUSIVE
) {
238 lockmgr(&devfs_rule_lock
, LK_EXCLUSIVE
);
242 TAILQ_FOREACH(rule
, &devfs_rule_list
, link
) {
244 * Skip this rule if it is only intended for jailed mount points
245 * and the current mount point isn't jailed
247 if ((rule
->rule_type
& DEVFS_RULE_JAIL
) &&
248 (!(DEVFS_MNTDATA(mp
)->jailed
)) )
252 * Skip this rule if it is not intended for jailed mount points
253 * and the current mount point is jailed.
255 if (!(rule
->rule_type
& DEVFS_RULE_JAIL
) &&
256 (DEVFS_MNTDATA(mp
)->jailed
))
260 * Skip this rule if the mount point specified in the rule doesn't
261 * match the mount point of the node
263 if ((rule
->mntpoint
[0] != '*') &&
264 (strcmp(rule
->mntpoint
, mp
->mnt_stat
.f_mntonname
)))
268 * Skip this rule if this is a by-type rule and the device flags
269 * don't match the specified device type in the rule
271 if ((rule
->rule_type
& DEVFS_RULE_TYPE
) &&
272 ( (rule
->dev_type
== 0) || (!dev_is_good(node
->d_dev
)) ||
273 (!(dev_dflags(node
->d_dev
) & rule
->dev_type
))) )
277 * Skip this rule if this is a by-name rule and the node name
278 * doesn't match the wildcard string in the rule
280 if ((rule
->rule_type
& DEVFS_RULE_NAME
) &&
281 (!devfs_rule_checkname(rule
, node
)) )
284 if (rule
->rule_cmd
& DEVFS_RULE_HIDE
) {
286 * If we should hide the device, we just apply the relevant
287 * hide flag to the node and let devfs do the rest in the
290 if ((node
->d_dir
.d_namlen
== 5) &&
291 (!memcmp(node
->d_dir
.d_name
, "devfs", 5))) {
293 * Magically avoid /dev/devfs from being hidden, so that one
294 * can still use the rule system even after a "* hide".
298 node
->flags
|= DEVFS_HIDDEN
;
300 } else if (rule
->rule_cmd
& DEVFS_RULE_SHOW
) {
302 * Show rule just means that the node should not be hidden, so
303 * what we do is clear the hide flag from the node.
305 node
->flags
&= ~DEVFS_HIDDEN
;
307 } else if (rule
->rule_cmd
& DEVFS_RULE_LINK
) {
309 * This is a LINK rule, so we tell devfs to create
310 * a link with the correct name to this node.
312 devfs_rule_create_link(node
, rule
);
314 devfs_alias_create(rule
->linkname
, node
, 1);
317 } else if (rule
->rule_cmd
& DEVFS_RULE_PERM
) {
319 * This is a normal ownership/permission rule. We
320 * just apply the permissions and ownership and
323 node
->mode
= rule
->mode
;
324 node
->uid
= rule
->uid
;
325 node
->gid
= rule
->gid
;
330 /* If we acquired the lock, we also get rid of it */
332 lockmgr(&devfs_rule_lock
, LK_RELEASE
);
339 devfs_rule_checkname(struct devfs_rule
*rule
, struct devfs_node
*node
)
341 struct devfs_node
*parent
= DEVFS_MNTDATA(node
->mp
)->root_node
;
343 char *name
, name_buf
[PATH_MAX
];
346 devfs_resolve_name_path(rule
->name
, name_buf
, &path
, &name
);
347 parent
= devfs_resolve_or_create_path(parent
, path
, 0);
350 return 0; /* no match */
352 /* Check if node is a child of the parent we found */
353 if (node
->parent
!= parent
)
354 return 0; /* no match */
357 if (rule
->rule_type
& DEVFS_RULE_LINK
)
358 no_match
= memcmp(name
, node
->d_dir
.d_name
, strlen(name
));
361 no_match
= WildCaseCmp(name
, node
->d_dir
.d_name
);
368 devfs_dev_open(struct dev_open_args
*ap
)
371 * Only allow read-write access.
373 if (((ap
->a_oflags
& FWRITE
) == 0) || ((ap
->a_oflags
& FREAD
) == 0))
377 * We don't allow nonblocking access.
379 if ((ap
->a_oflags
& O_NONBLOCK
) != 0) {
380 devfs_debug(DEVFS_DEBUG_SHOW
, "devfs_dev: can't do nonblocking access\n");
389 devfs_dev_close(struct dev_close_args
*ap
)
396 devfs_dev_ioctl(struct dev_ioctl_args
*ap
)
399 struct devfs_rule_ioctl
*rule
;
402 rule
= (struct devfs_rule_ioctl
*)ap
->a_data
;
406 devfs_rule_insert(rule
);
409 case DEVFS_RULE_APPLY
:
410 devfs_apply_rules(rule
->mntpoint
);
413 case DEVFS_RULE_CLEAR
:
414 devfs_rule_clear(rule
);
417 case DEVFS_RULE_RESET
:
418 devfs_reset_rules(rule
->mntpoint
);
422 error
= ENOTTY
; /* Inappropriate ioctl for device */
431 devfs_dev_init(void *unused
)
433 lockinit(&devfs_rule_lock
, "devfs_rule lock", 0, 0);
435 devfs_rule_cache
= objcache_create("devfs-rule-cache", 0, 0,
437 objcache_malloc_alloc
,
438 objcache_malloc_free
,
439 &devfs_rule_malloc_args
);
441 devfs_dev
= make_dev(&devfs_dev_ops
,
451 devfs_dev_uninit(void *unused
)
453 /* XXX: destroy all rules first */
454 destroy_dev(devfs_dev
);
455 objcache_destroy(devfs_rule_cache
);
459 SYSINIT(devfsdev
,SI_SUB_DRIVERS
,SI_ORDER_FIRST
,devfs_dev_init
,NULL
)
460 SYSUNINIT(devfsdev
, SI_SUB_DRIVERS
,SI_ORDER_FIRST
,devfs_dev_uninit
, NULL
);
465 WildCmp(const char *w
, const char *s
)
469 int slen
= strlen(s
);
472 for (i
= c
= 0; w
[i
]; ++i
) {
476 mary
= kmalloc(sizeof(char *) * (c
+ 1), M_DEVFS
, M_WAITOK
);
477 for (i
= 0; i
< c
; ++i
)
479 i
= wildCmp(mary
, 0, w
, s
);
480 kfree(mary
, M_DEVFS
);
487 WildCaseCmp(const char *w
, const char *s
)
491 int slen
= strlen(s
);
494 for (i
= c
= 0; w
[i
]; ++i
) {
498 mary
= kmalloc(sizeof(char *) * (c
+ 1), M_DEVFS
, M_WAITOK
);
499 for (i
= 0; i
< c
; ++i
)
501 i
= wildCaseCmp(mary
, 0, w
, s
);
502 kfree(mary
, M_DEVFS
);
507 * WildCmp() - compare wild string to sane string
509 * Returns 0 on success, -1 on failure.
512 wildCmp(const char **mary
, int d
, const char *w
, const char *s
)
523 * optimize terminator
527 if (w
[1] != '?' && w
[1] != '*') {
529 * optimize * followed by non-wild
531 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
532 if (s
[i
] == w
[1] && wildCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
539 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
540 if (wildCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
555 if (*w
== 0) /* terminator */
568 * WildCaseCmp() - compare wild string to sane string, case insensitive
570 * Returns 0 on success, -1 on failure.
573 wildCaseCmp(const char **mary
, int d
, const char *w
, const char *s
)
584 * optimize terminator
588 if (w
[1] != '?' && w
[1] != '*') {
590 * optimize * followed by non-wild
592 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
593 if (s
[i
] == w
[1] && wildCaseCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
600 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
601 if (wildCaseCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
615 #define tolower(x) ((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x))
616 if (tolower(*w
) != tolower(*s
))
619 if (*w
== 0) /* terminator */