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