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