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