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