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> 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> 47 48 MALLOC_DECLARE(M_DEVFS); 49 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); 56 57 static d_open_t devfs_dev_open; 58 static d_close_t devfs_dev_close; 59 static d_ioctl_t devfs_dev_ioctl; 60 61 static struct devfs_rule *devfs_rule_alloc(struct devfs_rule_ioctl *); 62 static void devfs_rule_free(struct devfs_rule *); 63 static int devfs_rule_insert(struct devfs_rule_ioctl *); 64 static void devfs_rule_remove(struct devfs_rule *); 65 static int 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 *); 68 69 static struct objcache *devfs_rule_cache; 70 static struct lock devfs_rule_lock; 71 72 static struct objcache_malloc_args devfs_rule_malloc_args = { 73 sizeof(struct devfs_rule), M_DEVFS }; 74 75 static cdev_t devfs_dev; 76 static struct devfs_rule_head devfs_rule_list = 77 TAILQ_HEAD_INITIALIZER(devfs_rule_list); 78 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 84 }; 85 86 87 static struct devfs_rule * 88 devfs_rule_alloc(struct devfs_rule_ioctl *templ) 89 { 90 struct devfs_rule *rule; 91 size_t len; 92 93 rule = objcache_get(devfs_rule_cache, M_WAITOK); 94 memset(rule, 0, sizeof(struct devfs_rule)); 95 96 if (templ->mntpoint == NULL) 97 goto error_out; 98 /* NOTREACHED */ 99 100 len = strlen(templ->mntpoint); 101 if (len == 0) 102 goto error_out; 103 /* NOTREACHED */ 104 105 rule->mntpoint = kstrdup(templ->mntpoint, M_DEVFS); 106 rule->mntpointlen = len; 107 108 if (templ->rule_type & DEVFS_RULE_NAME) { 109 if (templ->name == NULL) 110 goto error_out; 111 /* NOTREACHED */ 112 113 len = strlen(templ->name); 114 if (len == 0) 115 goto error_out; 116 /* NOTREACHED */ 117 118 rule->name = kstrdup(templ->name, M_DEVFS); 119 rule->namlen = len; 120 } 121 122 if (templ->rule_cmd & DEVFS_RULE_LINK) { 123 if (templ->linkname == NULL) 124 goto error_out; 125 /* NOTREACHED */ 126 127 len = strlen(templ->linkname); 128 if (len == 0) 129 goto error_out; 130 /* NOTREACHED */ 131 132 rule->linkname = kstrdup(templ->linkname, M_DEVFS); 133 rule->linknamlen = len; 134 } 135 136 rule->rule_type = templ->rule_type; 137 rule->rule_cmd = templ->rule_cmd; 138 rule->dev_type = templ->dev_type; 139 rule->mode = templ->mode; 140 rule->uid = templ->uid; 141 rule->gid = templ->gid; 142 143 return rule; 144 145 error_out: 146 devfs_rule_free(rule); 147 return NULL; 148 } 149 150 151 static void 152 devfs_rule_free(struct devfs_rule *rule) 153 { 154 if (rule->mntpoint != NULL) { 155 kfree(rule->mntpoint, M_DEVFS); 156 } 157 158 if (rule->name != NULL) { 159 kfree(rule->name, M_DEVFS); 160 } 161 162 if (rule->linkname != NULL) { 163 kfree(rule->linkname, M_DEVFS); 164 } 165 objcache_put(devfs_rule_cache, rule); 166 } 167 168 169 static int 170 devfs_rule_insert(struct devfs_rule_ioctl *templ) 171 { 172 struct devfs_rule *rule; 173 174 rule = devfs_rule_alloc(templ); 175 if (rule == NULL) 176 return EINVAL; 177 178 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE); 179 TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link); 180 lockmgr(&devfs_rule_lock, LK_RELEASE); 181 182 return 0; 183 } 184 185 186 static void 187 devfs_rule_remove(struct devfs_rule *rule) 188 { 189 TAILQ_REMOVE(&devfs_rule_list, rule, link); 190 devfs_rule_free(rule); 191 } 192 193 194 static int 195 devfs_rule_clear(struct devfs_rule_ioctl *templ) 196 { 197 struct devfs_rule *rule1, *rule2; 198 size_t mntpointlen; 199 200 if (templ->mntpoint == NULL) 201 return EINVAL; 202 203 mntpointlen = strlen(templ->mntpoint); 204 if (mntpointlen == 0) 205 return EINVAL; 206 207 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE); 208 TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) { 209 if ((templ->mntpoint[0] == '*') || 210 ( (mntpointlen == rule1->mntpointlen) && 211 (!memcmp(templ->mntpoint, rule1->mntpoint, mntpointlen)) )) { 212 devfs_rule_remove(rule1); 213 } 214 } 215 lockmgr(&devfs_rule_lock, LK_RELEASE); 216 217 return 0; 218 } 219 220 221 void * 222 devfs_rule_reset_node(struct devfs_node *node, void *unused) 223 { 224 /* 225 * Don't blindly unhide all devices, some, like unix98 pty masters, 226 * haven't been hidden by a rule. 227 */ 228 if (node->flags & DEVFS_RULE_HIDDEN) 229 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN); 230 231 if ((node->node_type == Plink) && (node->flags & DEVFS_RULE_CREATED)) { 232 KKASSERT(node->link_target); 233 node->flags &= ~DEVFS_RULE_CREATED; 234 --node->link_target->nlinks; 235 devfs_gc(node); 236 } else if ((node->node_type == Pdev) && (node->d_dev)) { 237 node->uid = node->d_dev->si_uid; 238 node->gid = node->d_dev->si_gid; 239 node->mode = node->d_dev->si_perms; 240 } 241 242 return NULL; 243 } 244 245 static void 246 devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule) 247 { 248 size_t len = 0; 249 char *path = NULL; 250 char *name, name_buf[PATH_MAX], buf[PATH_MAX]; 251 252 if (rule->name[rule->namlen-1] == '*') { 253 devfs_resolve_name_path(rule->name, name_buf, &path, &name); 254 len = strlen(name); 255 --len; 256 ksnprintf(buf, sizeof(buf), "%s%s", 257 rule->linkname, node->d_dir.d_name+len); 258 devfs_alias_create(buf, node, 1); 259 } else { 260 devfs_alias_create(rule->linkname, node, 1); 261 } 262 } 263 264 void * 265 devfs_rule_check_apply(struct devfs_node *node, void *unused) 266 { 267 struct devfs_rule *rule; 268 struct mount *mp = node->mp; 269 int locked = 0; 270 271 /* Check if it is locked already. if not, we acquire the devfs lock */ 272 if (!(lockstatus(&devfs_rule_lock, curthread)) == LK_EXCLUSIVE) { 273 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE); 274 locked = 1; 275 } 276 277 TAILQ_FOREACH(rule, &devfs_rule_list, link) { 278 /* 279 * Skip this rule if it is only intended for jailed mount points 280 * and the current mount point isn't jailed 281 */ 282 if ((rule->rule_type & DEVFS_RULE_JAIL) && 283 (!(DEVFS_MNTDATA(mp)->jailed)) ) 284 continue; 285 286 /* 287 * Skip this rule if it is not intended for jailed mount points 288 * and the current mount point is jailed. 289 */ 290 if (!(rule->rule_type & DEVFS_RULE_JAIL) && 291 (DEVFS_MNTDATA(mp)->jailed)) 292 continue; 293 294 /* 295 * Skip this rule if the mount point specified in the rule doesn't 296 * match the mount point of the node 297 */ 298 if ((rule->mntpoint[0] != '*') && 299 (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname))) 300 continue; 301 302 /* 303 * Skip this rule if this is a by-type rule and the device flags 304 * don't match the specified device type in the rule 305 */ 306 if ((rule->rule_type & DEVFS_RULE_TYPE) && 307 ( (rule->dev_type == 0) || (!dev_is_good(node->d_dev)) || 308 (!(dev_dflags(node->d_dev) & rule->dev_type))) ) 309 continue; 310 311 /* 312 * Skip this rule if this is a by-name rule and the node name 313 * doesn't match the wildcard string in the rule 314 */ 315 if ((rule->rule_type & DEVFS_RULE_NAME) && 316 (!devfs_rule_checkname(rule, node)) ) 317 continue; 318 319 if (rule->rule_cmd & DEVFS_RULE_HIDE) { 320 /* 321 * If we should hide the device, we just apply the relevant 322 * hide flag to the node and let devfs do the rest in the 323 * vnops 324 */ 325 if ((node->d_dir.d_namlen == 5) && 326 (!memcmp(node->d_dir.d_name, "devfs", 5))) { 327 /* 328 * Magically avoid /dev/devfs from being hidden, so that one 329 * can still use the rule system even after a "* hide". 330 */ 331 continue; 332 } 333 node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN); 334 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) { 335 /* 336 * Show rule just means that the node should not be hidden, so 337 * what we do is clear the hide flag from the node. 338 */ 339 node->flags &= ~DEVFS_HIDDEN; 340 } else if (rule->rule_cmd & DEVFS_RULE_LINK) { 341 /* 342 * This is a LINK rule, so we tell devfs to create 343 * a link with the correct name to this node. 344 */ 345 devfs_rule_create_link(node, rule); 346 #if 0 347 devfs_alias_create(rule->linkname, node, 1); 348 #endif 349 } else if (rule->rule_cmd & DEVFS_RULE_PERM) { 350 /* 351 * This is a normal ownership/permission rule. We 352 * just apply the permissions and ownership and 353 * we are done. 354 */ 355 node->mode = rule->mode; 356 node->uid = rule->uid; 357 node->gid = rule->gid; 358 } 359 } 360 361 /* If we acquired the lock, we also get rid of it */ 362 if (locked) 363 lockmgr(&devfs_rule_lock, LK_RELEASE); 364 365 return NULL; 366 } 367 368 369 static int 370 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node) 371 { 372 struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node; 373 char *path = NULL; 374 char *name, name_buf[PATH_MAX]; 375 int no_match = 0; 376 377 devfs_resolve_name_path(rule->name, name_buf, &path, &name); 378 parent = devfs_resolve_or_create_path(parent, path, 0); 379 380 if (parent == NULL) 381 return 0; /* no match */ 382 383 /* Check if node is a child of the parent we found */ 384 if (node->parent != parent) 385 return 0; /* no match */ 386 387 #if 0 388 if (rule->rule_type & DEVFS_RULE_LINK) 389 no_match = memcmp(name, node->d_dir.d_name, strlen(name)); 390 else 391 #endif 392 no_match = WildCaseCmp(name, node->d_dir.d_name); 393 394 return !no_match; 395 } 396 397 398 static int 399 devfs_dev_open(struct dev_open_args *ap) 400 { 401 /* 402 * Only allow read-write access. 403 */ 404 if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0)) 405 return(EPERM); 406 407 /* 408 * We don't allow nonblocking access. 409 */ 410 if ((ap->a_oflags & O_NONBLOCK) != 0) { 411 devfs_debug(DEVFS_DEBUG_SHOW, "devfs_dev: can't do nonblocking access\n"); 412 return(ENODEV); 413 } 414 415 return 0; 416 } 417 418 419 static int 420 devfs_dev_close(struct dev_close_args *ap) 421 { 422 return 0; 423 } 424 425 426 static int 427 devfs_dev_ioctl(struct dev_ioctl_args *ap) 428 { 429 int error; 430 struct devfs_rule_ioctl *rule; 431 432 error = 0; 433 rule = (struct devfs_rule_ioctl *)ap->a_data; 434 435 switch(ap->a_cmd) { 436 case DEVFS_RULE_ADD: 437 error = devfs_rule_insert(rule); 438 break; 439 440 case DEVFS_RULE_APPLY: 441 if (rule->mntpoint == NULL) 442 error = EINVAL; 443 else 444 devfs_apply_rules(rule->mntpoint); 445 break; 446 447 case DEVFS_RULE_CLEAR: 448 error = devfs_rule_clear(rule); 449 break; 450 451 case DEVFS_RULE_RESET: 452 if (rule->mntpoint == NULL) 453 error = EINVAL; 454 else 455 devfs_reset_rules(rule->mntpoint); 456 break; 457 458 default: 459 error = ENOTTY; /* Inappropriate ioctl for device */ 460 break; 461 } 462 463 return(error); 464 } 465 466 467 static void 468 devfs_dev_init(void *unused) 469 { 470 lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0); 471 472 devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0, 473 NULL, NULL, NULL, 474 objcache_malloc_alloc, 475 objcache_malloc_free, 476 &devfs_rule_malloc_args ); 477 478 devfs_dev = make_dev(&devfs_dev_ops, 479 0, 480 UID_ROOT, 481 GID_WHEEL, 482 0600, 483 "devfs"); 484 } 485 486 487 static void 488 devfs_dev_uninit(void *unused) 489 { 490 /* XXX: destroy all rules first */ 491 destroy_dev(devfs_dev); 492 objcache_destroy(devfs_rule_cache); 493 } 494 495 496 SYSINIT(devfsdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_init,NULL) 497 SYSUNINIT(devfsdev, SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_uninit, NULL); 498 499 #if 0 500 501 static int 502 WildCmp(const char *w, const char *s) 503 { 504 int i; 505 int c; 506 int slen = strlen(s); 507 const char **mary; 508 509 for (i = c = 0; w[i]; ++i) { 510 if (w[i] == '*') 511 ++c; 512 } 513 mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK); 514 for (i = 0; i < c; ++i) 515 mary[i] = s + slen; 516 i = wildCmp(mary, 0, w, s); 517 kfree(mary, M_DEVFS); 518 return(i); 519 } 520 521 #endif 522 523 static int 524 WildCaseCmp(const char *w, const char *s) 525 { 526 int i; 527 int c; 528 int slen = strlen(s); 529 const char **mary; 530 531 for (i = c = 0; w[i]; ++i) { 532 if (w[i] == '*') 533 ++c; 534 } 535 mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK); 536 for (i = 0; i < c; ++i) 537 mary[i] = s + slen; 538 i = wildCaseCmp(mary, 0, w, s); 539 kfree(mary, M_DEVFS); 540 return(i); 541 } 542 543 /* 544 * WildCmp() - compare wild string to sane string 545 * 546 * Returns 0 on success, -1 on failure. 547 */ 548 static int 549 wildCmp(const char **mary, int d, const char *w, const char *s) 550 { 551 int i; 552 553 /* 554 * skip fixed portion 555 */ 556 for (;;) { 557 switch(*w) { 558 case '*': 559 /* 560 * optimize terminator 561 */ 562 if (w[1] == 0) 563 return(0); 564 if (w[1] != '?' && w[1] != '*') { 565 /* 566 * optimize * followed by non-wild 567 */ 568 for (i = 0; s + i < mary[d]; ++i) { 569 if (s[i] == w[1] && wildCmp(mary, d + 1, w + 1, s + i) == 0) 570 return(0); 571 } 572 } else { 573 /* 574 * less-optimal 575 */ 576 for (i = 0; s + i < mary[d]; ++i) { 577 if (wildCmp(mary, d + 1, w + 1, s + i) == 0) 578 return(0); 579 } 580 } 581 mary[d] = s; 582 return(-1); 583 case '?': 584 if (*s == 0) 585 return(-1); 586 ++w; 587 ++s; 588 break; 589 default: 590 if (*w != *s) 591 return(-1); 592 if (*w == 0) /* terminator */ 593 return(0); 594 ++w; 595 ++s; 596 break; 597 } 598 } 599 /* not reached */ 600 return(-1); 601 } 602 603 604 /* 605 * WildCaseCmp() - compare wild string to sane string, case insensitive 606 * 607 * Returns 0 on success, -1 on failure. 608 */ 609 static int 610 wildCaseCmp(const char **mary, int d, const char *w, const char *s) 611 { 612 int i; 613 614 /* 615 * skip fixed portion 616 */ 617 for (;;) { 618 switch(*w) { 619 case '*': 620 /* 621 * optimize terminator 622 */ 623 if (w[1] == 0) 624 return(0); 625 if (w[1] != '?' && w[1] != '*') { 626 /* 627 * optimize * followed by non-wild 628 */ 629 for (i = 0; s + i < mary[d]; ++i) { 630 if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 631 return(0); 632 } 633 } else { 634 /* 635 * less-optimal 636 */ 637 for (i = 0; s + i < mary[d]; ++i) { 638 if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 639 return(0); 640 } 641 } 642 mary[d] = s; 643 return(-1); 644 case '?': 645 if (*s == 0) 646 return(-1); 647 ++w; 648 ++s; 649 break; 650 default: 651 if (*w != *s) { 652 #define tolower(x) ((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x)) 653 if (tolower(*w) != tolower(*s)) 654 return(-1); 655 } 656 if (*w == 0) /* terminator */ 657 return(0); 658 ++w; 659 ++s; 660 break; 661 } 662 } 663 /* not reached */ 664 return(-1); 665 } 666