1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2013 Damian Bogel. All rights reserved. 14 */ 15 16 /* 17 * Filesystem disturber pseudo-device driver. 18 */ 19 20 #include <sys/conf.h> 21 #include <sys/ddi.h> 22 #include <sys/file.h> 23 #include <sys/fsd.h> 24 #include <sys/fsh.h> 25 #include <sys/kmem.h> 26 #include <sys/ksynch.h> 27 #include <sys/list.h> 28 #include <sys/mkdev.h> 29 #include <sys/refstr.h> 30 #include <sys/stat.h> 31 #include <sys/sunddi.h> 32 #include <sys/sysmacros.h> 33 #include <sys/types.h> 34 35 /* 36 * fsd - filesystem disturber 37 * 38 * 1. Abstract 39 * Filesystem disturber is a pseudo-device driver used to inject pathological 40 * behaviour into vfs calls. It is NOT a fuzzer. That kind of behaviour 41 * should be expected and correctly handled by software. A simple example of 42 * such behaviour is read() reading less bytes than it was requested. It's 43 * well documented and every read() caller should check the return value of 44 * this function before proceeding. 45 * 46 * 2. Features 47 * * per-vfs injections 48 * * injection installing on every newly mounted vfs (that's called an 49 * omnipresent disturber) 50 * 51 * 3. Usage 52 * fsd_t is a structure which contains all the parameters for the disturbers. 53 * This structure is shared by all hooks on a vfs_t. 54 * 55 * fsd_info_t is filled out by a call to ioctl() and it provides basic 56 * information about fsd's current status. 57 * 58 * fsd_dis_t is passed to ioctl() when a request to disturb a filesystem is 59 * made. It's just a descriptor of a representative file and an fsd_t structure. 60 * 61 * fsd_fs_t is a structure filled out by ioctl() call when the client requests a 62 * full list of disturbers installed in the system. 63 * 64 * fsd_ioc_t is an union for different ioctl() commands. 65 * 66 * ioctl() commands: 67 * FSD_ENABLE: 68 * ioctl(fd, FSD_ENABLE); 69 * Enables the fsd. When fsd is enabled, any attemps to detach the driver 70 * will fail. 71 * 72 * FSD_DISABLE: 73 * ioctl(fd, FSD_DISABLE); 74 * Disables the fsd. 75 * 76 * FSD_GET_PARAM: 77 * ioctl(fd, FSD_GET_PARAM, ioc); 78 * Get's fsd_t associated with a given filesystem. ioc is fsdioc_mnt when 79 * passed to ioctl(). fsdioc_param is the output. 80 * Errors: 81 * ENOENT - the filesystem is not being disturbed 82 * 83 * FSD_DISTURB: 84 * ioctl(fd, FSD_DISTURB, ioc); 85 * Installs a disturber on a given filesystem. If a disturber is already 86 * installed on this filesystem, it overwrites it. ioc is fsdioc_dis. 87 * Errors: 88 * EAGAIN - hook limit exceeded 89 * EBADFD - cannot open the file descriptor 90 * EINVAL - parameters are invalid 91 * 92 * FSD_DISTURB_OFF: 93 * ioctl(fd, FSD_DISTURB_OFF, ioc); 94 * Removes a disturber from a given filesystem. ioc is fsdioc_mnt 95 * Errors: 96 * EBADFD - cannot open the file descriptor 97 * ENOENT - the filesystem is not being disturbed 98 * 99 * FSD_DISTURB_OMNI: 100 * ioctl(fd, FSD_DISTURB_OMNI, ioc); 101 * Install an omnipresent disturber. It means that whenever a new vfs_t is 102 * being created, this disturber is installed on it. If an omnipresent 103 * disturber is already installed, it overwrites it. ioc is fsdioc_param 104 * Errors: 105 * EINVAL - parameters are invalid 106 * 107 * FSD_DISTURB_OMNI_OFF: 108 * ioctl(fd, FSD_DISTURB_OMNI_OFF); 109 * Removes the omnipresent disturber. That does NOT mean that filesystems 110 * which are disturbed because of the omnipresent disturber presence in the 111 * past are going to stop being disturbed after this call. 112 * 113 * FSD_GET_LIST: 114 * ioctl(fd, FSD_GET_LIST, ioc); 115 * Get's a full list of disturbers installed in the system. ioc is 116 * fsdioc_list here. This is a structure with two fields, count and listp. 117 * The count is the number of fsd_fs_t's allocated on the address that 118 * listp is pointing to. There would be at most count fsd_fs_t entries 119 * copied out to the caller. Also, count is set to the number of entries 120 * copied out. 121 * 122 * FSD_GET_INFO: 123 * ioctl(fd, FSD_GET_INFO, ioc); 124 * Get's current information about fsd. ioc is fsdioc_info here. 125 * 126 * At most one hook is installed per vfs_t, and fsd_t describes all possible 127 * disturbance methods. Multiple commands using the fsd should somehow cooperate 128 * in order not to destroy each other efforts in installing disturbers. 129 * 130 * 4. Internals 131 * When fsd_enabled is nonzero, fsd_detach() fails. 132 * 133 * fsd_attach() does the necessary mount, free callbacks installing. 134 * fsd_detach() does the removing. 135 * These callbacks are used for both installing injections on newly mounted 136 * vfs_t's (omnipresent) and cleaning up when a vfs_t is destroyed. 137 * (fsd_callback_{mount, free}) 138 * 139 * The list of currently installed hooks is kept in fsd_list. 140 * 141 * fsd installs at most one hook on a vfs_t. 142 * 143 * 5. Locking 144 * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is 145 * protected by fsd_lock. fsd_lock should be acquired and released by a call to 146 * fsd_try_hold() and fsd_try_rele(). 147 * 148 * Hooks use only the elements of fsd_list, nothing else. Before an element of 149 * fsd_list is destroyed, a hook which uses it is removed. Elements from 150 * fsd_lists are removed and destroyed in the hook remove callback 151 * (fsd_remove_cb). 152 * 153 * Because of the fact that fsd_remove_cb could be called both in the context of 154 * the thread that executes fsd_remove_disturber() or outside the fsd, we need 155 * to use fsd_try_{hold,rele}() functions in order not to cause a deadlock. 156 * 157 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock. 158 */ 159 160 /* 161 * Once a set of hooks is installed on a filesystem, there's no need 162 * to bother fsh if we want to change the parameters of disturbance. 163 * Intead, we use fsd_lock to protect the fsd_int_t when it's being 164 * used or changed. 165 */ 166 typedef struct fsd_int { 167 krwlock_t fsdi_lock; /* protects fsd_param */ 168 fsd_t fsdi_param; 169 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */ 170 vfs_t *fsdi_vfsp; 171 list_node_t fsdi_next; 172 } fsd_int_t; 173 174 static dev_info_t *fsd_devi; 175 176 177 /* procets: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle */ 178 static kmutex_t fsd_lock; 179 static kthread_t *fsd_owner; 180 static kmutex_t fsd_owner_lock; 181 182 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */ 183 static fsh_callback_handle_t fsd_cb_handle; 184 static int fsd_enabled; 185 186 /* 187 * List of fsd_int_t. For every vfs_t on which fsd has installed a set of hooks 188 * there exist exactly one fsd_int_t with fsdi_vfsp pointing to this vfs_t. 189 */ 190 static list_t fsd_list; 191 static int fsd_list_count; 192 static kcondvar_t fsd_cv_empty; 193 194 195 /* 196 * Although it's safe to use this kind of pseudo-random number generator, 197 * it behaves very regular when it comes to parity. Every fsd_rand() call 198 * changes the parity of the seed. That's why when a range of width 2 is set 199 * as a parameter, it's highly possible that the random value will always be 200 * the same, because fsd_rand() could be called the same number of times in a 201 * hook. 202 */ 203 static long fsd_rand_seed; 204 205 static int 206 fsd_rand() 207 { 208 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345; 209 return (fsd_rand_seed & 0x7ffffffff); 210 } 211 212 /* vnode hooks */ 213 /* 214 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx() 215 * call, because it's valid until the hooks associated with it are removed. 216 * If a hook is removed, it cannot be executing. 217 */ 218 static int 219 fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop, 220 int ioflag, cred_t *cr, caller_context_t *ct) 221 { 222 fsd_int_t *fsdi = (fsd_int_t *)arg; 223 uint64_t count, less, less_chance; 224 225 /* 226 * It is used to keep an odd number of fsd_rand() calls in every 227 * fsd_hook_read() call. That is desired because when a range of width 228 * 2 is set as a parameter, we don't want to make it a constant. 229 * The pseudo-random number generator returns a number with different 230 * parity with every call. If this function is called in every 231 * fsd_hook_read() execution even number of times, it would always be 232 * the same % 2. 233 */ 234 (void) fsd_rand(); 235 236 ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp); 237 238 rw_enter(&fsdi->fsdi_lock, RW_READER); 239 less_chance = fsdi->fsdi_param.read_less_chance; 240 less = (uint64_t)fsd_rand() % 241 (fsdi->fsdi_param.read_less_r[1] + 1 - 242 fsdi->fsdi_param.read_less_r[0]) + fsdi->fsdi_param.read_less_r[0]; 243 rw_exit(&fsdi->fsdi_lock); 244 245 count = uiop->uio_iov->iov_len; 246 if ((uint64_t)fsd_rand() % 100 < less_chance) { 247 extern size_t copyout_max_cached; 248 int ret; 249 250 if (count > less) 251 count -= less; 252 else 253 less = 0; 254 255 uiop->uio_iov->iov_len = count; 256 uiop->uio_resid = count; 257 if (count <= copyout_max_cached) 258 uiop->uio_extflg = UIO_COPY_CACHED; 259 else 260 uiop->uio_extflg = UIO_COPY_DEFAULT; 261 262 ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct); 263 uiop->uio_resid += less; 264 return (ret); 265 } 266 267 return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct)); 268 } 269 270 /* 271 * These functions are used for fsd_lock locking. 272 */ 273 static void 274 fsd_try_hold() 275 { 276 int should_lock; 277 278 mutex_enter(&fsd_owner_lock); 279 should_lock = fsd_owner != curthread; 280 mutex_exit(&fsd_owner_lock); 281 282 if (should_lock) { 283 mutex_enter(&fsd_lock); 284 mutex_enter(&fsd_owner_lock); 285 fsd_owner = curthread; 286 mutex_exit(&fsd_owner_lock); 287 } 288 } 289 290 static void 291 fsd_try_rele() 292 { 293 int should_free; 294 295 mutex_enter(&fsd_owner_lock); 296 should_free = fsd_owner == curthread; 297 mutex_exit(&fsd_owner_lock); 298 299 if (should_free) { 300 mutex_enter(&fsd_owner_lock); 301 fsd_owner = NULL; 302 mutex_exit(&fsd_owner_lock); 303 mutex_exit(&fsd_lock); 304 } 305 306 307 } 308 309 static void 310 fsd_remove_cb(void *arg, fsh_handle_t handle) 311 { 312 _NOTE(ARGUNUSED(handle)); 313 314 fsd_int_t *fsdi = (fsd_int_t *)arg; 315 fsd_try_hold(); 316 317 list_remove(&fsd_list, fsdi); 318 fsd_list_count--; 319 if (fsd_list_count == 0) 320 cv_signal(&fsd_cv_empty); 321 322 rw_destroy(&fsdi->fsdi_lock); 323 kmem_free(fsdi, sizeof (*fsdi)); 324 325 fsd_try_rele(); 326 } 327 328 /* 329 * Installs a set of hook with given parameters on a vfs_t. 330 * 331 * It is expected that fsd_lock is being held. 332 * 333 * Returns 0 on success and non-zero if hook limit exceeded. 334 */ 335 static int 336 fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd) 337 { 338 fsd_int_t *fsdi; 339 340 ASSERT(MUTEX_HELD(&fsd_lock)); 341 342 for (fsdi = list_head(&fsd_list); fsdi != NULL; 343 fsdi = list_next(&fsd_list, fsdi)) { 344 if (fsdi->fsdi_vfsp == vfsp) 345 break; 346 } 347 348 if (fsdi != NULL) { 349 /* Just change the existing fsd_int_t */ 350 rw_enter(&fsdi->fsdi_lock, RW_WRITER); 351 (void) memcpy(&fsdi->fsdi_param, fsd, 352 sizeof (fsdi->fsdi_param)); 353 rw_exit(&fsdi->fsdi_lock); 354 } else { 355 fsh_t hook = { 0 }; 356 357 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP); 358 fsdi->fsdi_vfsp = vfsp; 359 (void) memcpy(&fsdi->fsdi_param, fsd, 360 sizeof (fsdi->fsdi_param)); 361 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL); 362 363 hook.arg = fsdi; 364 hook.read = fsd_hook_read; 365 hook.remove_cb = fsd_remove_cb; 366 367 /* 368 * It is safe to do so, because none of the hooks installed 369 * by fsd uses fsdi_handle nor the fsd_list. 370 */ 371 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook); 372 if (fsdi->fsdi_handle == -1) { 373 kmem_free(fsdi, sizeof (*fsdi)); 374 rw_destroy(&fsdi->fsdi_lock); 375 return (-1); 376 } 377 list_insert_head(&fsd_list, fsdi); 378 fsd_list_count++; 379 } 380 return (0); 381 } 382 383 static int 384 fsd_remove_disturber(vfs_t *vfsp) 385 { 386 fsd_int_t *fsdi; 387 388 ASSERT(MUTEX_HELD(&fsd_lock)); 389 390 for (fsdi = list_head(&fsd_list); fsdi != NULL; 391 fsdi = list_next(&fsd_list, fsdi)) { 392 if (fsdi->fsdi_vfsp == vfsp) 393 break; 394 } 395 if (fsdi == NULL) 396 return (ENOENT); 397 398 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 399 400 return (0); 401 } 402 403 static void 404 fsd_callback_mount(vfs_t *vfsp, void *arg) 405 { 406 _NOTE(ARGUNUSED(arg)); 407 408 int error = 0; 409 410 fsd_try_hold(); 411 if (fsd_omni_param != NULL) 412 error = fsd_install_disturber(vfsp, fsd_omni_param); 413 fsd_try_rele(); 414 415 if (error != 0) { 416 refstr_t *mntref; 417 418 mntref = vfs_getmntpoint(vfsp); 419 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n", 420 refstr_value(mntref)); 421 refstr_rele(mntref); 422 } 423 } 424 425 static void 426 fsd_callback_free(vfs_t *vfsp, void *arg) 427 { 428 _NOTE(ARGUNUSED(arg)); 429 430 fsd_int_t *fsdi; 431 432 /* 433 * Why we don't just pass fsd_int_t associated with this hook as an 434 * argument? 435 * Let's say that this callback has been just fired, but hasn't yet 436 * locked the fsd_lock. Meanwhile, in other thread, 437 * fsd_remove_disturber() is executing and the hook associated with 438 * fsd_int_t has been removed and the fsd_int_t has been destroyed. Now 439 * we go back to our free callback thread, and we try to remove an entry 440 * which does not exist. 441 */ 442 fsd_try_hold(); 443 for (fsdi = list_head(&fsd_list); fsdi != NULL; 444 fsdi = list_next(&fsd_list, fsdi)) { 445 if (fsdi->fsdi_vfsp == vfsp) { 446 /* 447 * We make such assertion, because fsd_lock is held 448 * and that means that neither fsd_remove_disturber() 449 * nor fsd_remove_cb() has removed this hook in 450 * different thread. 451 */ 452 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 453 454 /* 455 * Since there is at most one hook installed by fsd, 456 * we break. 457 */ 458 break; 459 } 460 } 461 /* 462 * We can't write ASSERT(fsdi != NULL) because it is possible that 463 * there was a concurrent call to fsd_remove_disturber() or 464 * fsd_detach(). 465 */ 466 fsd_try_rele(); 467 } 468 469 470 static void 471 fsd_enable() 472 { 473 fsd_try_hold(); 474 fsd_enabled = 1; 475 fsd_try_rele(); 476 } 477 478 static void 479 fsd_disable() 480 { 481 fsd_try_hold(); 482 fsd_enabled = 0; 483 fsd_try_rele(); 484 } 485 486 487 /* Entry points */ 488 static int 489 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 490 { 491 minor_t instance; 492 fsh_callback_t cb = { 0 }; 493 494 if (cmd != DDI_ATTACH) 495 return (DDI_FAILURE); 496 497 if (fsd_devi != NULL) 498 return (DDI_FAILURE); 499 500 instance = ddi_get_instance(dip); 501 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance, 502 DDI_PSEUDO, 0) == DDI_FAILURE) 503 return (DDI_FAILURE); 504 fsd_devi = dip; 505 ddi_report_dev(fsd_devi); 506 507 list_create(&fsd_list, sizeof (fsd_int_t), 508 offsetof(fsd_int_t, fsdi_next)); 509 510 fsd_rand_seed = gethrtime(); 511 512 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL); 513 mutex_init(&fsd_owner_lock, NULL, MUTEX_DRIVER, NULL); 514 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL); 515 516 cb.fshc_mount = fsd_callback_mount; 517 cb.fshc_free = fsd_callback_free; 518 cb.fshc_arg = fsd_omni_param; 519 fsd_cb_handle = fsh_callback_install(&cb); 520 if (fsd_cb_handle == -1) { 521 /* Cleanup */ 522 list_destroy(&fsd_list); 523 cv_destroy(&fsd_cv_empty); 524 mutex_destroy(&fsd_owner_lock); 525 mutex_destroy(&fsd_lock); 526 ddi_remove_minor_node(fsd_devi, NULL); 527 fsd_devi = NULL; 528 return (DDI_FAILURE); 529 } 530 531 return (DDI_SUCCESS); 532 } 533 534 /* 535 * If fsd_enable() was called and there was no subsequent fsd_disable() call, 536 * detach will fail. 537 */ 538 static int 539 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 540 { 541 fsd_int_t *fsdi; 542 543 if (cmd != DDI_DETACH) 544 return (DDI_FAILURE); 545 546 ASSERT(dip == fsd_devi); 547 548 /* 549 * No need to hold fsd_lock here. Since only the hooks and callbacks 550 * might be running at this point. 551 */ 552 if (fsd_enabled) 553 return (DDI_FAILURE); 554 555 ddi_remove_minor_node(dip, NULL); 556 fsd_devi = NULL; 557 558 /* 559 * Hooks have to be removed before the callbacks. That's because without 560 * free() callbacks, we wouldn't be able to determine if a hook handle 561 * is valid. 562 */ 563 fsd_try_hold(); 564 for (fsdi = list_head(&fsd_list); fsdi != NULL; 565 fsdi = list_next(&fsd_list, fsdi)) { 566 ASSERT(fsd_remove_disturber(fsdi->fsdi_vfsp) == 0); 567 } 568 569 while (fsd_list_count > 0) 570 cv_wait(&fsd_cv_empty, &fsd_lock); 571 fsd_try_rele(); 572 cv_destroy(&fsd_cv_empty); 573 574 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0); 575 if (fsd_omni_param != NULL) { 576 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 577 fsd_omni_param = NULL; 578 } 579 580 /* After removing the callbacks and hooks, it is safe to remove these */ 581 list_destroy(&fsd_list); 582 mutex_destroy(&fsd_owner_lock); 583 mutex_destroy(&fsd_lock); 584 585 return (DDI_SUCCESS); 586 } 587 588 static int 589 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 590 { 591 _NOTE(ARGUNUSED(dip)); 592 593 switch (infocmd) { 594 case DDI_INFO_DEVT2DEVINFO: 595 *resultp = fsd_devi; 596 return (DDI_SUCCESS); 597 case DDI_INFO_DEVT2INSTANCE: 598 *resultp = (void *)(uintptr_t)getminor((dev_t)arg); 599 return (DDI_SUCCESS); 600 default: 601 return (DDI_FAILURE); 602 } 603 } 604 605 static int 606 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp) 607 { 608 _NOTE(ARGUNUSED(devp)); 609 610 if (flag & FEXCL || flag & FNDELAY) 611 return (EINVAL); 612 613 if (otyp != OTYP_CHR) 614 return (EINVAL); 615 616 if (!(flag & FREAD && flag & FWRITE)) 617 return (EINVAL); 618 619 if (drv_priv(credp) == EPERM) 620 return (EPERM); 621 622 return (0); 623 } 624 625 static int 626 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp) 627 { 628 _NOTE(ARGUNUSED(dev)); 629 _NOTE(ARGUNUSED(flag)); 630 _NOTE(ARGUNUSED(otyp)); 631 _NOTE(ARGUNUSED(credp)); 632 633 return (0); 634 } 635 636 637 /* ioctl(9E) and it's support functions */ 638 static int 639 fsd_check_param(fsd_t *fsd) 640 { 641 if (fsd->read_less_chance > 100 || 642 fsd->read_less_r[0] > fsd->read_less_r[1]) 643 return (EINVAL); 644 return (0); 645 } 646 647 static int 648 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp) 649 { 650 file_t *file; 651 fsd_dis_t dis; 652 int rv; 653 654 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode)) 655 return (EFAULT); 656 657 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) { 658 *rvalp = rv; 659 return (0); 660 } 661 662 if ((file = getf((int)dis.fsdd_mnt)) == NULL) { 663 *rvalp = EBADFD; 664 return (0); 665 } 666 667 mutex_enter(&fsd_lock); 668 rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param); 669 mutex_exit(&fsd_lock); 670 671 releasef((int)dis.fsdd_mnt); 672 673 if (rv != 0) 674 *rvalp = EAGAIN; 675 else 676 *rvalp = 0; 677 678 return (0); 679 } 680 681 static int 682 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp) 683 { 684 file_t *file; 685 fsd_int_t *fsdi; 686 int error = 0; 687 int64_t fd; 688 vfs_t *vfsp; 689 690 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 691 return (EFAULT); 692 693 if ((file = getf((int)fd)) == NULL) { 694 *rvalp = EBADFD; 695 return (0); 696 } 697 vfsp = file->f_vnode->v_vfsp; 698 releasef((int)fd); 699 700 701 mutex_enter(&fsd_lock); 702 703 for (fsdi = list_head(&fsd_list); fsdi != NULL; 704 fsdi = list_next(&fsd_list, fsdi)) { 705 if (fsdi->fsdi_vfsp == vfsp) 706 break; 707 } 708 if (fsdi == NULL) { 709 *rvalp = ENOENT; 710 mutex_exit(&fsd_lock); 711 return (0); 712 } 713 rw_enter(&fsdi->fsdi_lock, RW_READER); 714 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param, 715 sizeof (fsdi->fsdi_param), mode); 716 rw_exit(&fsdi->fsdi_lock); 717 718 mutex_exit(&fsd_lock); 719 720 721 if (error) { 722 return (EFAULT); 723 } else { 724 *rvalp = 0; 725 return (0); 726 } 727 } 728 729 static int 730 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp) 731 { 732 fsd_info_t info; 733 734 fsd_try_hold(); 735 info.fsdinf_enabled = fsd_enabled; 736 info.fsdinf_count = fsd_list_count; 737 info.fsdinf_omni_on = fsd_omni_param != NULL; 738 if (info.fsdinf_omni_on) 739 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param, 740 sizeof (info.fsdinf_omni_param)); 741 fsd_try_rele(); 742 743 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode)) 744 return (EFAULT); 745 746 *rvalp = 0; 747 return (0); 748 } 749 750 static int 751 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp) 752 { 753 fsd_int_t *fsdi; 754 fsd_fs_t *fsdfs_list; 755 int i; 756 int ret = 0; 757 int64_t ioc_list_count; 758 759 *rvalp = 0; 760 761 /* Get data */ 762 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count, 763 sizeof (ioc_list_count), mode)) 764 return (EFAULT); 765 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list, 766 sizeof (fsdfs_list), mode)) 767 return (EFAULT); 768 769 770 fsd_try_hold(); 771 if (ioc_list_count > fsd_list_count) 772 ioc_list_count = fsd_list_count; 773 774 /* Copyout */ 775 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count, 776 sizeof (ioc_list_count), mode)) { 777 ret = EFAULT; 778 goto out; 779 } 780 for (fsdi = list_head(&fsd_list), i = 0; 781 fsdi != NULL && i < ioc_list_count; 782 fsdi = list_next(&fsd_list, fsdi), i++) { 783 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp); 784 int len = strlen(refstr_value(mntstr)); 785 786 rw_enter(&fsdi->fsdi_lock, RW_READER); 787 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name, 788 len + 1, mode) || 789 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param, 790 sizeof (fsdi->fsdi_param), mode)) { 791 ret = EFAULT; 792 } 793 rw_exit(&fsdi->fsdi_lock); 794 refstr_rele(mntstr); 795 796 if (ret != 0) 797 break; 798 } 799 800 801 out: 802 fsd_try_rele(); 803 return (ret); 804 } 805 806 static int 807 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp) 808 { 809 file_t *file; 810 int64_t fd; 811 812 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 813 return (EFAULT); 814 815 if ((file = getf((int)fd)) == NULL) { 816 *rvalp = EBADFD; 817 return (0); 818 } 819 820 fsd_try_hold(); 821 *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp); 822 releasef((int)fd); 823 fsd_try_rele(); 824 825 return (0); 826 } 827 828 static int 829 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp) 830 { 831 fsd_t fsd; 832 int rv; 833 834 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode)) 835 return (EFAULT); 836 837 if ((rv = fsd_check_param(&fsd)) != 0) { 838 *rvalp = rv; 839 return (0); 840 } 841 842 fsd_try_hold(); 843 if (fsd_omni_param == NULL) 844 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param), 845 KM_SLEEP); 846 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param)); 847 fsd_try_rele(); 848 849 *rvalp = 0; 850 return (0); 851 } 852 853 854 static int 855 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 856 int *rvalp) 857 { 858 _NOTE(ARGUNUSED(dev)); 859 _NOTE(ARGUNUSED(credp)); 860 861 if (!fsd_enabled && cmd != FSD_ENABLE) { 862 *rvalp = ENOTACTIVE; 863 return (0); 864 } 865 866 switch (cmd) { 867 case FSD_ENABLE: 868 fsd_enable(); 869 *rvalp = 0; 870 return (0); 871 872 case FSD_DISABLE: 873 fsd_disable(); 874 *rvalp = 0; 875 return (0); 876 877 case FSD_GET_PARAM: 878 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp)); 879 880 case FSD_DISTURB: 881 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp)); 882 883 case FSD_DISTURB_OFF: 884 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp)); 885 886 case FSD_DISTURB_OMNI: 887 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp)); 888 889 case FSD_DISTURB_OMNI_OFF: 890 fsd_try_hold(); 891 if (fsd_omni_param != NULL) 892 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 893 fsd_omni_param = NULL; 894 fsd_try_rele(); 895 896 *rvalp = 0; 897 return (0); 898 899 case FSD_GET_LIST: 900 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp)); 901 902 case FSD_GET_INFO: 903 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp)); 904 905 default: 906 return (ENOTTY); 907 } 908 } 909 910 static struct cb_ops cb_ops = { 911 fsd_open, /* open(9E) */ 912 fsd_close, /* close(9E) */ 913 nodev, /* strategy(9E) */ 914 nodev, /* print(9E) */ 915 nodev, /* dump(9E) */ 916 nodev, /* read(9E) */ 917 nodev, /* write(9E) */ 918 fsd_ioctl, /* ioctl(9E) */ 919 nodev, /* devmap(9E) */ 920 nodev, /* mmap(9E) */ 921 nodev, /* segmap(9E) */ 922 nochpoll, /* chpoll(9E) */ 923 ddi_prop_op, /* prop_op(9E) */ 924 NULL, /* streamtab(9E) */ 925 D_MP | D_64BIT, /* cb_flag(9E) */ 926 CB_REV, /* cb_rev(9E) */ 927 nodev, /* aread(9E) */ 928 nodev, /* awrite(9E) */ 929 }; 930 931 static struct dev_ops dev_ops = { 932 DEVO_REV, /* driver build version */ 933 0, /* reference count */ 934 fsd_getinfo, /* getinfo */ 935 nulldev, 936 nulldev, /* probe */ 937 fsd_attach, /* attach */ 938 fsd_detach, /* detach */ 939 nodev, 940 &cb_ops, /* cb_ops */ 941 NULL, /* bus_ops */ 942 NULL, /* power */ 943 ddi_quiesce_not_needed, /* quiesce */ 944 }; 945 946 static struct modldrv modldrv = { 947 &mod_driverops, "Filesystem disturber", &dev_ops 948 }; 949 950 static struct modlinkage modlinkage = { 951 MODREV_1, &modldrv, NULL 952 }; 953 954 int 955 _init(void) 956 { 957 return (mod_install(&modlinkage)); 958 } 959 960 int 961 _info(struct modinfo *modinfop) 962 { 963 return (mod_info(&modlinkage, modinfop)); 964 } 965 966 int 967 _fini(void) 968 { 969 return (mod_remove(&modlinkage)); 970 }