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 * The locking is fairly simple. Every modification of fsd_enable, fsd_hooks, 145 * fsd_omni_param and fsd_list is protected by fsd_lock. Hooks use only the 146 * elements of fsd_list, nothing else. Before an element of fsd_list 147 * is destroyed, a hook which uses it is removed. fsh guarantees that after a 148 * hook removal, it won't be executing. 149 * 150 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock. 151 */ 152 153 /* 154 * Once a set of hooks is installed on a filesystem, there's no need 155 * to bother fsh if we want to change the parameters of disturbance. 156 * Intead, we use fsd_lock to protect the fsd_int_t when it's being 157 * used or changed. 158 */ 159 typedef struct fsd_int { 160 krwlock_t fsdi_lock; /* protects fsd_param */ 161 fsd_t fsdi_param; 162 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */ 163 vfs_t *fsdi_vfsp; 164 list_node_t fsdi_next; 165 } fsd_int_t; 166 167 static dev_info_t *fsd_devi; 168 169 170 /* procets: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle */ 171 static kmutex_t fsd_lock; 172 173 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */ 174 static fsh_callback_handle_t fsd_cb_handle; 175 static int fsd_enabled; 176 177 /* 178 * List of fsd_int_t. For every vfs_t on which fsd has installed a set of hooks 179 * there exist exactly one fsd_int_t with fsdi_vfsp pointing to this vfs_t. 180 */ 181 static list_t fsd_list; 182 static int fsd_list_count; 183 184 185 /* 186 * Although it's safe to use this kind of pseudo-random number generator, 187 * it behaves very regular when it comes to parity. Every fsd_rand() call 188 * changes the parity of the seed. That's why when a range of width 2 is set 189 * as a parameter, it's highly possible that the random value will always be 190 * the same, because fsd_rand() could be called the same number of times in a 191 * hook. 192 */ 193 static long fsd_rand_seed; 194 195 static int 196 fsd_rand() 197 { 198 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345; 199 return (fsd_rand_seed & 0x7ffffffff); 200 } 201 202 /* vnode hooks */ 203 /* 204 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx() 205 * call, because it's valid until the hooks associated with it are removed. 206 * If a hook is removed, it cannot be executing. 207 */ 208 static int 209 fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop, 210 int ioflag, cred_t *cr, caller_context_t *ct) 211 { 212 fsd_int_t *fsdi = (fsd_int_t *)arg; 213 uint64_t count, less, less_chance; 214 215 /* 216 * It is used to keep an odd number of fsd_rand() calls in every 217 * fsd_hook_read() call. That is desired because when a range of width 218 * 2 is set as a parameter, we don't want to make it a constant. 219 * The pseudo-random number generator returns a number with different 220 * parity with every call. If this function is called in every 221 * fsd_hook_read() execution even number of times, it would always be 222 * the same % 2. 223 */ 224 (void) fsd_rand(); 225 226 ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp); 227 228 rw_enter(&fsdi->fsdi_lock, RW_READER); 229 less_chance = fsdi->fsdi_param.read_less_chance; 230 less = (uint64_t)fsd_rand() % 231 (fsdi->fsdi_param.read_less_r[1] + 1 - 232 fsdi->fsdi_param.read_less_r[0]) + fsdi->fsdi_param.read_less_r[0]; 233 rw_exit(&fsdi->fsdi_lock); 234 235 count = uiop->uio_iov->iov_len; 236 if ((uint64_t)fsd_rand() % 100 < less_chance) { 237 extern size_t copyout_max_cached; 238 int ret; 239 240 if (count > less) 241 count -= less; 242 else 243 less = 0; 244 245 uiop->uio_iov->iov_len = count; 246 uiop->uio_resid = count; 247 if (count <= copyout_max_cached) 248 uiop->uio_extflg = UIO_COPY_CACHED; 249 else 250 uiop->uio_extflg = UIO_COPY_DEFAULT; 251 252 ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct); 253 uiop->uio_resid += less; 254 return (ret); 255 } 256 257 return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct)); 258 } 259 260 /* 261 * Installs a set of hook with given parameters on a vfs_t. 262 * 263 * It is expected that fsd_lock is being held. 264 * 265 * Returns 0 on success and non-zero if hook limit exceeded. 266 */ 267 static int 268 fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd) 269 { 270 fsd_int_t *fsdi; 271 272 ASSERT(MUTEX_HELD(&fsd_lock)); 273 274 for (fsdi = list_head(&fsd_list); fsdi != NULL; 275 fsdi = list_next(&fsd_list, fsdi)) { 276 if (fsdi->fsdi_vfsp == vfsp) 277 break; 278 } 279 280 if (fsdi != NULL) { 281 /* Just change the existing fsd_int_t */ 282 rw_enter(&fsdi->fsdi_lock, RW_WRITER); 283 (void) memcpy(&fsdi->fsdi_param, fsd, 284 sizeof (fsdi->fsdi_param)); 285 rw_exit(&fsdi->fsdi_lock); 286 } else { 287 fsh_t hook = { 0 }; 288 289 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP); 290 fsdi->fsdi_vfsp = vfsp; 291 (void) memcpy(&fsdi->fsdi_param, fsd, 292 sizeof (fsdi->fsdi_param)); 293 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL); 294 295 hook.arg = fsdi; 296 hook.read = fsd_hook_read; 297 298 /* 299 * It is safe to do so, because none of the hooks installed 300 * by fsd uses fsdi_handle nor the fsd_list. 301 */ 302 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook); 303 if (fsdi->fsdi_handle == -1) { 304 kmem_free(fsdi, sizeof (*fsdi)); 305 rw_destroy(&fsdi->fsdi_lock); 306 return (-1); 307 } 308 list_insert_head(&fsd_list, fsdi); 309 fsd_list_count++; 310 } 311 return (0); 312 } 313 314 static int 315 fsd_remove_disturber(vfs_t *vfsp) 316 { 317 fsd_int_t *fsdi; 318 319 ASSERT(MUTEX_HELD(&fsd_lock)); 320 321 for (fsdi = list_head(&fsd_list); fsdi != NULL; 322 fsdi = list_next(&fsd_list, fsdi)) { 323 if (fsdi->fsdi_vfsp == vfsp) 324 break; 325 } 326 if (fsdi == NULL) 327 return (ENOENT); 328 329 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 330 list_remove(&fsd_list, fsdi); 331 fsd_list_count--; 332 ASSERT(fsd_list_count >= 0); 333 rw_destroy(&fsdi->fsdi_lock); 334 kmem_free(fsdi, sizeof (*fsdi)); 335 336 return (0); 337 } 338 339 static void 340 fsd_callback_mount(vfs_t *vfsp, void *arg) 341 { 342 _NOTE(ARGUNUSED(arg)); 343 344 int error = 0; 345 346 mutex_enter(&fsd_lock); 347 if (fsd_omni_param != NULL) 348 error = fsd_install_disturber(vfsp, fsd_omni_param); 349 mutex_exit(&fsd_lock); 350 351 if (error != 0) { 352 refstr_t *mntref; 353 354 mntref = vfs_getmntpoint(vfsp); 355 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n", 356 refstr_value(mntref)); 357 refstr_rele(mntref); 358 } 359 } 360 361 static void 362 fsd_callback_free(vfs_t *vfsp, void *arg) 363 { 364 _NOTE(ARGUNUSED(arg)); 365 366 fsd_int_t *fsdi; 367 368 /* 369 * Why we don't just pass fsd_int_t associated with this hook as an 370 * argument? 371 * Let's say that this callback has been just fired, but hasn't yet 372 * locked the fsd_lock. Meanwhile, in other thread, 373 * fsd_remove_disturber() is executing and the hook associated with 374 * fsd_int_t has been removed and the fsd_int_t has been destroyed. Now 375 * we go back to our free callback thread, and we try to remove an entry 376 * which does not exist. Furthermore, we have no information about 377 * whether the deletion was successful, so we use it to get the 378 * fsdi_handle, but obviously, fsdi is now garbage. 379 */ 380 mutex_enter(&fsd_lock); 381 for (fsdi = list_head(&fsd_list); fsdi != NULL; 382 fsdi = list_next(&fsd_list, fsdi)) { 383 if (fsdi->fsdi_vfsp == vfsp) { 384 /* 385 * We can make such assertion, because fsd_lock is held 386 * and that means that fsd_remove_disturber() hasn't 387 * removed this hook in different thread. 388 */ 389 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 390 list_remove(&fsd_list, fsdi); 391 fsd_list_count--; 392 rw_destroy(&fsdi->fsdi_lock); 393 kmem_free(fsdi, sizeof (*fsdi)); 394 395 /* 396 * Since there is at most one hook installed by fsd, 397 * we break. 398 */ 399 break; 400 } 401 } 402 /* 403 * We can't write ASSERT(fsdi != NULL) because it is possible that 404 * there was a concurrent call to fsd_remove_disturber() or 405 * fsd_detach(). 406 */ 407 mutex_exit(&fsd_lock); 408 } 409 410 411 static void 412 fsd_enable() 413 { 414 mutex_enter(&fsd_lock); 415 fsd_enabled = 1; 416 mutex_exit(&fsd_lock); 417 } 418 419 static void 420 fsd_disable() 421 { 422 mutex_enter(&fsd_lock); 423 fsd_enabled = 0; 424 mutex_exit(&fsd_lock); 425 } 426 427 428 /* Entry points */ 429 static int 430 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 431 { 432 minor_t instance; 433 fsh_callback_t cb = { 0 }; 434 435 if (cmd != DDI_ATTACH) 436 return (DDI_FAILURE); 437 438 if (fsd_devi != NULL) 439 return (DDI_FAILURE); 440 441 instance = ddi_get_instance(dip); 442 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance, 443 DDI_PSEUDO, 0) == DDI_FAILURE) 444 return (DDI_FAILURE); 445 fsd_devi = dip; 446 ddi_report_dev(fsd_devi); 447 448 list_create(&fsd_list, sizeof (fsd_int_t), 449 offsetof(fsd_int_t, fsdi_next)); 450 451 fsd_rand_seed = gethrtime(); 452 453 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL); 454 455 cb.fshc_mount = fsd_callback_mount; 456 cb.fshc_free = fsd_callback_free; 457 cb.fshc_arg = fsd_omni_param; 458 fsd_cb_handle = fsh_callback_install(&cb); 459 if (fsd_cb_handle == -1) { 460 /* Cleanup */ 461 list_destroy(&fsd_list); 462 mutex_destroy(&fsd_lock); 463 ddi_remove_minor_node(fsd_devi, NULL); 464 fsd_devi = NULL; 465 return (DDI_FAILURE); 466 } 467 468 return (DDI_SUCCESS); 469 } 470 471 /* 472 * If fsd_enable() was called and there was no subsequent fsd_disable() call, 473 * detach will fail. 474 */ 475 static int 476 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 477 { 478 fsd_int_t *fsdi; 479 480 if (cmd != DDI_DETACH) 481 return (DDI_FAILURE); 482 483 ASSERT(dip == fsd_devi); 484 485 /* 486 * No need to hold fsd_lock here. Since only the hooks and callbacks 487 * might be running at this point. 488 */ 489 if (fsd_enabled) 490 return (DDI_FAILURE); 491 492 ddi_remove_minor_node(dip, NULL); 493 fsd_devi = NULL; 494 495 /* 496 * Hooks have to be removed before the callbacks. That's because without 497 * free() callbacks, we wouldn't be able to determine if a hook handle 498 * is valid. 499 */ 500 mutex_enter(&fsd_lock); 501 while ((fsdi = list_head(&fsd_list)) != NULL) { 502 /* 503 * fsd_remove_disturber() removes the fsdi from the head of 504 * fsd_list. 505 */ 506 ASSERT(fsd_remove_disturber(fsdi->fsdi_vfsp) == 0); 507 } 508 mutex_exit(&fsd_lock); 509 510 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0); 511 if (fsd_omni_param != NULL) { 512 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 513 fsd_omni_param = NULL; 514 } 515 516 /* After removing the callbacks and hooks, it is safe to remove these */ 517 list_destroy(&fsd_list); 518 mutex_destroy(&fsd_lock); 519 520 return (DDI_SUCCESS); 521 } 522 523 static int 524 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 525 { 526 _NOTE(ARGUNUSED(dip)); 527 528 switch (infocmd) { 529 case DDI_INFO_DEVT2DEVINFO: 530 *resultp = fsd_devi; 531 return (DDI_SUCCESS); 532 case DDI_INFO_DEVT2INSTANCE: 533 *resultp = (void *)(uintptr_t)getminor((dev_t)arg); 534 return (DDI_SUCCESS); 535 default: 536 return (DDI_FAILURE); 537 } 538 } 539 540 static int 541 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp) 542 { 543 _NOTE(ARGUNUSED(devp)); 544 545 if (flag & FEXCL || flag & FNDELAY) 546 return (EINVAL); 547 548 if (otyp != OTYP_CHR) 549 return (EINVAL); 550 551 if (!(flag & FREAD && flag & FWRITE)) 552 return (EINVAL); 553 554 if (drv_priv(credp) == EPERM) 555 return (EPERM); 556 557 return (0); 558 } 559 560 static int 561 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp) 562 { 563 _NOTE(ARGUNUSED(dev)); 564 _NOTE(ARGUNUSED(flag)); 565 _NOTE(ARGUNUSED(otyp)); 566 _NOTE(ARGUNUSED(credp)); 567 568 return (0); 569 } 570 571 572 /* ioctl(9E) and it's support functions */ 573 static int 574 fsd_check_param(fsd_t *fsd) 575 { 576 if (fsd->read_less_chance > 100 || 577 fsd->read_less_r[0] > fsd->read_less_r[1]) 578 return (EINVAL); 579 return (0); 580 } 581 582 static int 583 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp) 584 { 585 file_t *file; 586 fsd_dis_t dis; 587 int rv; 588 589 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode)) 590 return (EFAULT); 591 592 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) { 593 *rvalp = rv; 594 return (0); 595 } 596 597 if ((file = getf((int)dis.fsdd_mnt)) == NULL) { 598 *rvalp = EBADFD; 599 return (0); 600 } 601 602 mutex_enter(&fsd_lock); 603 rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param); 604 mutex_exit(&fsd_lock); 605 606 releasef((int)dis.fsdd_mnt); 607 608 if (rv != 0) 609 *rvalp = EAGAIN; 610 else 611 *rvalp = 0; 612 613 return (0); 614 } 615 616 static int 617 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp) 618 { 619 file_t *file; 620 fsd_int_t *fsdi; 621 int error = 0; 622 int64_t fd; 623 vfs_t *vfsp; 624 625 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 626 return (EFAULT); 627 628 if ((file = getf((int)fd)) == NULL) { 629 *rvalp = EBADFD; 630 return (0); 631 } 632 vfsp = file->f_vnode->v_vfsp; 633 releasef((int)fd); 634 635 636 mutex_enter(&fsd_lock); 637 638 for (fsdi = list_head(&fsd_list); fsdi != NULL; 639 fsdi = list_next(&fsd_list, fsdi)) { 640 if (fsdi->fsdi_vfsp == vfsp) 641 break; 642 } 643 if (fsdi == NULL) { 644 *rvalp = ENOENT; 645 mutex_exit(&fsd_lock); 646 return (0); 647 } 648 rw_enter(&fsdi->fsdi_lock, RW_READER); 649 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param, 650 sizeof (fsdi->fsdi_param), mode); 651 rw_exit(&fsdi->fsdi_lock); 652 653 mutex_exit(&fsd_lock); 654 655 656 if (error) { 657 return (EFAULT); 658 } else { 659 *rvalp = 0; 660 return (0); 661 } 662 } 663 664 static int 665 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp) 666 { 667 fsd_info_t info; 668 669 mutex_enter(&fsd_lock); 670 info.fsdinf_enabled = fsd_enabled; 671 info.fsdinf_count = fsd_list_count; 672 info.fsdinf_omni_on = fsd_omni_param != NULL; 673 if (info.fsdinf_omni_on) 674 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param, 675 sizeof (info.fsdinf_omni_param)); 676 mutex_exit(&fsd_lock); 677 678 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode)) 679 return (EFAULT); 680 681 *rvalp = 0; 682 return (0); 683 } 684 685 static int 686 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp) 687 { 688 fsd_int_t *fsdi; 689 fsd_fs_t *fsdfs_list; 690 int i; 691 int ret = 0; 692 int64_t ioc_list_count; 693 694 *rvalp = 0; 695 696 /* Get data */ 697 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count, 698 sizeof (ioc_list_count), mode)) 699 return (EFAULT); 700 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list, 701 sizeof (fsdfs_list), mode)) 702 return (EFAULT); 703 704 705 mutex_enter(&fsd_lock); 706 707 if (ioc_list_count > fsd_list_count) 708 ioc_list_count = fsd_list_count; 709 710 /* Copyout */ 711 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count, 712 sizeof (ioc_list_count), mode)) { 713 ret = EFAULT; 714 goto out; 715 } 716 for (fsdi = list_head(&fsd_list), i = 0; 717 fsdi != NULL && i < ioc_list_count; 718 fsdi = list_next(&fsd_list, fsdi), i++) { 719 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp); 720 int len = strlen(refstr_value(mntstr)); 721 722 rw_enter(&fsdi->fsdi_lock, RW_READER); 723 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name, 724 len + 1, mode) || 725 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param, 726 sizeof (fsdi->fsdi_param), mode)) { 727 ret = EFAULT; 728 } 729 rw_exit(&fsdi->fsdi_lock); 730 refstr_rele(mntstr); 731 732 if (ret != 0) 733 break; 734 } 735 736 737 out: 738 mutex_exit(&fsd_lock); 739 return (ret); 740 } 741 742 static int 743 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp) 744 { 745 file_t *file; 746 int64_t fd; 747 748 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 749 return (EFAULT); 750 751 if ((file = getf((int)fd)) == NULL) { 752 *rvalp = EBADFD; 753 return (0); 754 } 755 756 mutex_enter(&fsd_lock); 757 *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp); 758 releasef((int)fd); 759 mutex_exit(&fsd_lock); 760 761 return (0); 762 } 763 764 static int 765 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp) 766 { 767 fsd_t fsd; 768 int rv; 769 770 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode)) 771 return (EFAULT); 772 773 if ((rv = fsd_check_param(&fsd)) != 0) { 774 *rvalp = rv; 775 return (0); 776 } 777 778 mutex_enter(&fsd_lock); 779 if (fsd_omni_param == NULL) 780 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param), 781 KM_SLEEP); 782 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param)); 783 mutex_exit(&fsd_lock); 784 785 *rvalp = 0; 786 return (0); 787 } 788 789 790 static int 791 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 792 int *rvalp) 793 { 794 _NOTE(ARGUNUSED(dev)); 795 _NOTE(ARGUNUSED(credp)); 796 797 if (!fsd_enabled && cmd != FSD_ENABLE) { 798 *rvalp = ENOTACTIVE; 799 return (0); 800 } 801 802 switch (cmd) { 803 case FSD_ENABLE: 804 fsd_enable(); 805 *rvalp = 0; 806 return (0); 807 808 case FSD_DISABLE: 809 fsd_disable(); 810 *rvalp = 0; 811 return (0); 812 813 case FSD_GET_PARAM: 814 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp)); 815 816 case FSD_DISTURB: 817 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp)); 818 819 case FSD_DISTURB_OFF: 820 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp)); 821 822 case FSD_DISTURB_OMNI: 823 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp)); 824 825 case FSD_DISTURB_OMNI_OFF: 826 mutex_enter(&fsd_lock); 827 if (fsd_omni_param != NULL) 828 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 829 fsd_omni_param = NULL; 830 mutex_exit(&fsd_lock); 831 832 *rvalp = 0; 833 return (0); 834 835 case FSD_GET_LIST: 836 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp)); 837 838 case FSD_GET_INFO: 839 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp)); 840 841 default: 842 return (ENOTTY); 843 } 844 } 845 846 static struct cb_ops cb_ops = { 847 fsd_open, /* open(9E) */ 848 fsd_close, /* close(9E) */ 849 nodev, /* strategy(9E) */ 850 nodev, /* print(9E) */ 851 nodev, /* dump(9E) */ 852 nodev, /* read(9E) */ 853 nodev, /* write(9E) */ 854 fsd_ioctl, /* ioctl(9E) */ 855 nodev, /* devmap(9E) */ 856 nodev, /* mmap(9E) */ 857 nodev, /* segmap(9E) */ 858 nochpoll, /* chpoll(9E) */ 859 ddi_prop_op, /* prop_op(9E) */ 860 NULL, /* streamtab(9E) */ 861 D_MP | D_64BIT, /* cb_flag(9E) */ 862 CB_REV, /* cb_rev(9E) */ 863 nodev, /* aread(9E) */ 864 nodev, /* awrite(9E) */ 865 }; 866 867 static struct dev_ops dev_ops = { 868 DEVO_REV, /* driver build version */ 869 0, /* reference count */ 870 fsd_getinfo, /* getinfo */ 871 nulldev, 872 nulldev, /* probe */ 873 fsd_attach, /* attach */ 874 fsd_detach, /* detach */ 875 nodev, 876 &cb_ops, /* cb_ops */ 877 NULL, /* bus_ops */ 878 NULL, /* power */ 879 ddi_quiesce_not_needed, /* quiesce */ 880 }; 881 882 static struct modldrv modldrv = { 883 &mod_driverops, "Filesystem disturber", &dev_ops 884 }; 885 886 static struct modlinkage modlinkage = { 887 MODREV_1, &modldrv, NULL 888 }; 889 890 int 891 _init(void) 892 { 893 return (mod_install(&modlinkage)); 894 } 895 896 int 897 _info(struct modinfo *modinfop) 898 { 899 return (mod_info(&modlinkage, modinfop)); 900 } 901 902 int 903 _fini(void) 904 { 905 return (mod_remove(&modlinkage)); 906 }