AMD64 - Move fdisk and nextboot up one level and remove src/sbin/i386.
[dragonfly.git] / sys / vfs / devfs / devfs_rules.c
blob9a4cf2d78477991baacdec2fbfcc2e2714204c4e
1 /*
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
9 * are met:
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.
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.
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>
48 MALLOC_DECLARE(M_DEVFS);
50 #if 0
51 static int WildCmp(const char *w, const char *s);
52 #endif
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 = {
80 { "devfs", 0, 0 },
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;
91 size_t len;
93 rule = objcache_get(devfs_rule_cache, M_WAITOK);
94 memset(rule, 0, sizeof(struct devfs_rule));
96 len = strlen(templ->mntpoint);
97 if (len > 0) {
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);
104 if (len > 0) {
105 rule->name = kstrdup(templ->name, M_DEVFS);
106 rule->namlen = len;
110 if (templ->rule_cmd == DEVFS_RULE_LINK) {
111 len = strlen(templ->linkname);
112 if (len > 0) {
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;
125 return rule;
129 static void
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);
147 static void
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);
160 static void
161 devfs_rule_remove(struct devfs_rule *rule)
163 TAILQ_REMOVE(&devfs_rule_list, rule, link);
164 devfs_rule_free(rule);
168 static void
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);
186 void *
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;
199 devfs_gc(node);
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;
206 return NULL;
209 static void
210 devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule)
212 size_t len = 0;
213 char *path = NULL;
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);
218 len = strlen(name);
219 --len;
220 ksnprintf(buf, sizeof(buf), "%s%s",
221 rule->linkname, node->d_dir.d_name+len);
222 devfs_alias_create(buf, node, 1);
223 } else {
224 devfs_alias_create(rule->linkname, node, 1);
228 void *
229 devfs_rule_check_apply(struct devfs_node *node, void *unused)
231 struct devfs_rule *rule;
232 struct mount *mp = node->mp;
233 int applies = 0;
234 int locked = 0;
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);
239 locked = 1;
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)) )
249 continue;
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))
257 continue;
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)))
265 continue;
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))) )
274 continue;
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)) )
282 continue;
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
288 * vnops
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".
296 continue;
298 node->flags |= DEVFS_HIDDEN;
299 applies = 1;
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;
306 applies = 1;
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);
313 #if 0
314 devfs_alias_create(rule->linkname, node, 1);
315 #endif
316 applies = 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
321 * we are done.
323 node->mode = rule->mode;
324 node->uid = rule->uid;
325 node->gid = rule->gid;
326 applies = 1;
330 /* If we acquired the lock, we also get rid of it */
331 if (locked)
332 lockmgr(&devfs_rule_lock, LK_RELEASE);
334 return NULL;
338 static int
339 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
341 struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
342 char *path = NULL;
343 char *name, name_buf[PATH_MAX];
344 int no_match = 0;
346 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
347 parent = devfs_resolve_or_create_path(parent, path, 0);
349 if (parent == NULL)
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 */
356 #if 0
357 if (rule->rule_type & DEVFS_RULE_LINK)
358 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
359 else
360 #endif
361 no_match = WildCaseCmp(name, node->d_dir.d_name);
363 return !no_match;
367 static int
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))
374 return(EPERM);
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");
381 return(ENODEV);
384 return 0;
388 static int
389 devfs_dev_close(struct dev_close_args *ap)
391 return 0;
395 static int
396 devfs_dev_ioctl(struct dev_ioctl_args *ap)
398 int error;
399 struct devfs_rule_ioctl *rule;
401 error = 0;
402 rule = (struct devfs_rule_ioctl *)ap->a_data;
404 switch(ap->a_cmd) {
405 case DEVFS_RULE_ADD:
406 devfs_rule_insert(rule);
407 break;
409 case DEVFS_RULE_APPLY:
410 devfs_apply_rules(rule->mntpoint);
411 break;
413 case DEVFS_RULE_CLEAR:
414 devfs_rule_clear(rule);
415 break;
417 case DEVFS_RULE_RESET:
418 devfs_reset_rules(rule->mntpoint);
419 break;
421 default:
422 error = ENOTTY; /* Inappropriate ioctl for device */
423 break;
426 return(error);
430 static void
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,
436 NULL, NULL, NULL,
437 objcache_malloc_alloc,
438 objcache_malloc_free,
439 &devfs_rule_malloc_args );
441 devfs_dev = make_dev(&devfs_dev_ops,
443 UID_ROOT,
444 GID_WHEEL,
445 0600,
446 "devfs");
450 static void
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);
462 #if 0
464 static int
465 WildCmp(const char *w, const char *s)
467 int i;
468 int c;
469 int slen = strlen(s);
470 const char **mary;
472 for (i = c = 0; w[i]; ++i) {
473 if (w[i] == '*')
474 ++c;
476 mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
477 for (i = 0; i < c; ++i)
478 mary[i] = s + slen;
479 i = wildCmp(mary, 0, w, s);
480 kfree(mary, M_DEVFS);
481 return(i);
484 #endif
486 static int
487 WildCaseCmp(const char *w, const char *s)
489 int i;
490 int c;
491 int slen = strlen(s);
492 const char **mary;
494 for (i = c = 0; w[i]; ++i) {
495 if (w[i] == '*')
496 ++c;
498 mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
499 for (i = 0; i < c; ++i)
500 mary[i] = s + slen;
501 i = wildCaseCmp(mary, 0, w, s);
502 kfree(mary, M_DEVFS);
503 return(i);
507 * WildCmp() - compare wild string to sane string
509 * Returns 0 on success, -1 on failure.
511 static int
512 wildCmp(const char **mary, int d, const char *w, const char *s)
514 int i;
517 * skip fixed portion
519 for (;;) {
520 switch(*w) {
521 case '*':
523 * optimize terminator
525 if (w[1] == 0)
526 return(0);
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)
533 return(0);
535 } else {
537 * less-optimal
539 for (i = 0; s + i < mary[d]; ++i) {
540 if (wildCmp(mary, d + 1, w + 1, s + i) == 0)
541 return(0);
544 mary[d] = s;
545 return(-1);
546 case '?':
547 if (*s == 0)
548 return(-1);
549 ++w;
550 ++s;
551 break;
552 default:
553 if (*w != *s)
554 return(-1);
555 if (*w == 0) /* terminator */
556 return(0);
557 ++w;
558 ++s;
559 break;
562 /* not reached */
563 return(-1);
568 * WildCaseCmp() - compare wild string to sane string, case insensitive
570 * Returns 0 on success, -1 on failure.
572 static int
573 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
575 int i;
578 * skip fixed portion
580 for (;;) {
581 switch(*w) {
582 case '*':
584 * optimize terminator
586 if (w[1] == 0)
587 return(0);
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)
594 return(0);
596 } else {
598 * less-optimal
600 for (i = 0; s + i < mary[d]; ++i) {
601 if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
602 return(0);
605 mary[d] = s;
606 return(-1);
607 case '?':
608 if (*s == 0)
609 return(-1);
610 ++w;
611 ++s;
612 break;
613 default:
614 if (*w != *s) {
615 #define tolower(x) ((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x))
616 if (tolower(*w) != tolower(*s))
617 return(-1);
619 if (*w == 0) /* terminator */
620 return(0);
621 ++w;
622 ++s;
623 break;
626 /* not reached */
627 return(-1);