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