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). The free callback is used for cleaning up. 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_disturber_remove() 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_node; 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 void 236 fsd_hook_pre_read(void *arg, void **instancep, vnode_t **vpp, uio_t **uiopp, 237 int *ioflagp, cred_t **crp, caller_context_t **ctp) 238 { 239 _NOTE(ARGUNUSED(ioflagp)); 240 _NOTE(ARGUNUSED(crp)); 241 _NOTE(ARGUNUSED(ctp)); 242 243 fsd_int_t *fsdi = (fsd_int_t *)arg; 244 uint64_t less_chance; 245 246 /* 247 * It is used to keep an odd number of fsd_rand() calls in every 248 * fsd_hook_pre_read() call. That is desired because when a range of 249 * width 2 is set as a parameter, we don't want to make it a constant. 250 * The pseudo-random number generator returns a number with different 251 * parity with every call. If this function is called in every 252 * fsd_hook_pre_read() execution even number of times, it would always 253 * be the same % 2. 254 */ 255 (void) fsd_rand(); 256 257 ASSERT((*vpp)->v_vfsp == fsdi->fsdi_vfsp); 258 259 rw_enter(&fsdi->fsdi_lock, RW_READER); 260 less_chance = fsdi->fsdi_param.read_less_chance; 261 rw_exit(&fsdi->fsdi_lock); 262 263 if ((uint64_t)fsd_rand() % 100 < less_chance) { 264 extern size_t copyout_max_cached; 265 uint64_t r[2]; 266 uint64_t count, less; 267 268 count = (*uiopp)->uio_iov->iov_len; 269 r[0] = fsdi->fsdi_param.read_less_r[0]; 270 r[1] = fsdi->fsdi_param.read_less_r[1]; 271 less = (uint64_t)fsd_rand() % (r[1] + 1 - r[0]) + r[0]; 272 273 if (count > less) { 274 count -= less; 275 *instancep = kmem_alloc(sizeof (uint64_t), KM_SLEEP); 276 *(*(uint64_t **)instancep) = less; 277 } else { 278 *instancep = NULL; 279 return; 280 } 281 282 (*uiopp)->uio_iov->iov_len = count; 283 (*uiopp)->uio_resid = count; 284 if (count <= copyout_max_cached) 285 (*uiopp)->uio_extflg = UIO_COPY_CACHED; 286 else 287 (*uiopp)->uio_extflg = UIO_COPY_DEFAULT; 288 } else { 289 *instancep = NULL; 290 } 291 } 292 293 static int 294 fsd_hook_post_read(int ret, void *arg, void *instance, vnode_t *vp, 295 uio_t *uiop, int oflag, cred_t *cr, caller_context_t *ct) 296 { 297 _NOTE(ARGUNUSED(arg)); 298 _NOTE(ARGUNUSED(vp)); 299 _NOTE(ARGUNUSED(oflag)); 300 _NOTE(ARGUNUSED(cr)); 301 _NOTE(ARGUNUSED(ct)); 302 303 if (instance != NULL) { 304 uint64_t *lessp = instance; 305 uiop->uio_resid += *lessp; 306 kmem_free(lessp, sizeof (*lessp)); 307 } 308 return (ret); 309 } 310 311 static void 312 fsd_remove_cb(void *arg, fsh_handle_t handle) 313 { 314 _NOTE(ARGUNUSED(handle)); 315 316 fsd_int_t *fsdi = (fsd_int_t *)arg; 317 int fsd_context; 318 319 mutex_enter(&fsd_rem_thread_lock); 320 fsd_context = fsd_rem_thread == curthread; 321 mutex_exit(&fsd_rem_thread_lock); 322 323 if (!fsd_context) 324 mutex_enter(&fsd_lock); 325 326 ASSERT(MUTEX_HELD(&fsd_lock)); 327 328 if (!fsd_detaching) 329 list_remove(&fsd_list, fsdi); 330 331 rw_destroy(&fsdi->fsdi_lock); 332 kmem_free(fsdi, sizeof (*fsdi)); 333 334 fsd_list_count--; 335 if (fsd_list_count == 0) 336 cv_signal(&fsd_cv_empty); 337 338 if (!fsd_context) 339 mutex_exit(&fsd_lock); 340 } 341 342 /* 343 * Installs a set of hook with given parameters on a vfs_t. 344 * 345 * It is expected that fsd_lock is being held. 346 * 347 * Returns 0 on success and non-zero if hook limit exceeded. 348 */ 349 static int 350 fsd_disturber_install(vfs_t *vfsp, fsd_t *fsd) 351 { 352 fsd_int_t *fsdi; 353 354 ASSERT(MUTEX_HELD(&fsd_lock)); 355 356 for (fsdi = list_head(&fsd_list); fsdi != NULL; 357 fsdi = list_next(&fsd_list, fsdi)) { 358 if (fsdi->fsdi_vfsp == vfsp) 359 break; 360 } 361 362 if (fsdi != NULL) { 363 /* Just change the existing fsd_int_t */ 364 rw_enter(&fsdi->fsdi_lock, RW_WRITER); 365 (void) memcpy(&fsdi->fsdi_param, fsd, 366 sizeof (fsdi->fsdi_param)); 367 rw_exit(&fsdi->fsdi_lock); 368 } else { 369 fsh_t hook = { 0 }; 370 371 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP); 372 fsdi->fsdi_vfsp = vfsp; 373 (void) memcpy(&fsdi->fsdi_param, fsd, 374 sizeof (fsdi->fsdi_param)); 375 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL); 376 377 hook.arg = fsdi; 378 hook.pre_read = fsd_hook_pre_read; 379 hook.post_read = fsd_hook_post_read; 380 hook.remove_cb = fsd_remove_cb; 381 382 /* 383 * It is safe to do so, because none of the hooks installed 384 * by fsd uses fsdi_handle nor the fsd_list. 385 */ 386 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook); 387 if (fsdi->fsdi_handle == -1) { 388 kmem_free(fsdi, sizeof (*fsdi)); 389 rw_destroy(&fsdi->fsdi_lock); 390 return (-1); 391 } 392 list_insert_head(&fsd_list, fsdi); 393 fsd_list_count++; 394 } 395 return (0); 396 } 397 398 static int 399 fsd_disturber_remove(vfs_t *vfsp) 400 { 401 fsd_int_t *fsdi; 402 403 ASSERT(MUTEX_HELD(&fsd_lock)); 404 405 for (fsdi = list_head(&fsd_list); fsdi != NULL; 406 fsdi = list_next(&fsd_list, fsdi)) { 407 if (fsdi->fsdi_vfsp == vfsp) 408 break; 409 } 410 if (fsdi == NULL || fsdi->fsdi_doomed) 411 return (ENOENT); 412 413 fsdi->fsdi_doomed = 1; 414 415 mutex_enter(&fsd_rem_thread_lock); 416 fsd_rem_thread = curthread; 417 mutex_exit(&fsd_rem_thread_lock); 418 419 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 420 421 mutex_enter(&fsd_rem_thread_lock); 422 fsd_rem_thread = NULL; 423 mutex_exit(&fsd_rem_thread_lock); 424 425 return (0); 426 } 427 428 static void 429 fsd_mount_callback(vfs_t *vfsp, void *arg) 430 { 431 _NOTE(ARGUNUSED(arg)); 432 433 int error = 0; 434 435 mutex_enter(&fsd_lock); 436 if (fsd_omni_param != NULL) 437 error = fsd_disturber_install(vfsp, fsd_omni_param); 438 mutex_exit(&fsd_lock); 439 440 if (error != 0) { 441 refstr_t *mntref; 442 443 mntref = vfs_getmntpoint(vfsp); 444 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n", 445 refstr_value(mntref)); 446 refstr_rele(mntref); 447 } 448 } 449 450 /* 451 * Although, we might delete the fsd_free_callback(), it would make the whole 452 * proces less clear. There's a time window between firing free callbacks and 453 * freeing the vfs_t in fsd_disturber_remove() could be called. fsh can 454 * deal with invalid handles (until there is no collision), but we'd like to 455 * have a nice assertion instead. 456 */ 457 static void 458 fsd_free_callback(vfs_t *vfsp, void *arg) 459 { 460 _NOTE(ARGUNUSED(arg)); 461 462 fsd_int_t *fsdi; 463 464 mutex_enter(&fsd_lock); 465 for (fsdi = list_head(&fsd_list); fsdi != NULL; 466 fsdi = list_next(&fsd_list, fsdi)) { 467 if (fsdi->fsdi_vfsp == vfsp) { 468 if (fsdi->fsdi_doomed) 469 continue; 470 471 fsdi->fsdi_doomed = 1; 472 /* 473 * We make such assertion, because fsd_lock is held 474 * and that means that neither fsd_disturber_remove() 475 * nor fsd_remove_cb() has removed this hook in 476 * different thread. 477 */ 478 mutex_enter(&fsd_rem_thread_lock); 479 fsd_rem_thread = curthread; 480 mutex_exit(&fsd_rem_thread_lock); 481 482 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 483 484 mutex_enter(&fsd_rem_thread_lock); 485 fsd_rem_thread = NULL; 486 mutex_exit(&fsd_rem_thread_lock); 487 488 /* 489 * Since there is at most one hook installed by fsd, 490 * we break. 491 */ 492 break; 493 } 494 } 495 /* 496 * We can't write ASSERT(fsdi != NULL) because it is possible that 497 * there was a concurrent call to fsd_disturber_remove() or 498 * fsd_detach(). 499 */ 500 mutex_exit(&fsd_lock); 501 } 502 503 static void 504 fsd_enable() 505 { 506 mutex_enter(&fsd_lock); 507 fsd_enabled = 1; 508 mutex_exit(&fsd_lock); 509 } 510 511 static void 512 fsd_disable() 513 { 514 mutex_enter(&fsd_lock); 515 fsd_enabled = 0; 516 mutex_exit(&fsd_lock); 517 } 518 519 520 /* Entry points */ 521 static int 522 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 523 { 524 minor_t instance; 525 fsh_callback_t cb = { 0 }; 526 527 if (cmd != DDI_ATTACH) 528 return (DDI_FAILURE); 529 530 if (fsd_devi != NULL) 531 return (DDI_FAILURE); 532 533 instance = ddi_get_instance(dip); 534 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance, 535 DDI_PSEUDO, 0) == DDI_FAILURE) 536 return (DDI_FAILURE); 537 fsd_devi = dip; 538 ddi_report_dev(fsd_devi); 539 540 list_create(&fsd_list, sizeof (fsd_int_t), 541 offsetof(fsd_int_t, fsdi_node)); 542 543 fsd_rand_seed = gethrtime(); 544 545 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL); 546 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL); 547 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL); 548 549 cb.fshc_mount = fsd_mount_callback; 550 cb.fshc_free = fsd_free_callback; 551 cb.fshc_arg = fsd_omni_param; 552 fsd_cb_handle = fsh_callback_install(&cb); 553 if (fsd_cb_handle == -1) { 554 /* Cleanup */ 555 list_destroy(&fsd_list); 556 cv_destroy(&fsd_cv_empty); 557 mutex_destroy(&fsd_rem_thread_lock); 558 mutex_destroy(&fsd_lock); 559 ddi_remove_minor_node(fsd_devi, NULL); 560 fsd_devi = NULL; 561 return (DDI_FAILURE); 562 } 563 564 return (DDI_SUCCESS); 565 } 566 567 /* 568 * If fsd_enable() was called and there was no subsequent fsd_disable() call, 569 * detach will fail. 570 */ 571 static int 572 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 573 { 574 fsd_int_t *fsdi; 575 576 if (cmd != DDI_DETACH) 577 return (DDI_FAILURE); 578 579 ASSERT(dip == fsd_devi); 580 581 /* 582 * No need to hold fsd_lock here. Since only the hooks and callbacks 583 * might be running at this point. 584 */ 585 if (fsd_enabled) 586 return (DDI_FAILURE); 587 588 ddi_remove_minor_node(dip, NULL); 589 fsd_devi = NULL; 590 591 /* 592 * 1. Remove the hooks. 593 * 2. Remove the callbacks. 594 * 595 * This order has to be preserved, because of the fact that 596 * fsd_free_callback() is the last stop before a vfs_t is destroyed. 597 * Without it, this might happen: 598 * vfs_free() fsd_detach() 599 * 1. Handle for the hook is 600 * invalidated. 601 * 2. Fired fsd_remove_cb(). 602 * 3. fsd_remove_cb() hasn't yet fsd_lock is acquired. 603 * acquired the fsd_lock. 604 * 4 Waiting for fsd_lock. That ASSERT(fsh_hook_remove(..) == 0); 605 * means that the hook hasn't failed, because the handle is 606 * been removed from fsd_hooks already invalid. 607 * fsd_hooks yet. 608 * 609 * The ASSERT() here is nice and without a good reason, we don't want 610 * to get rid of it. 611 */ 612 mutex_enter(&fsd_lock); 613 /* 614 * After we set fsd_detaching to 1, hook remove callback (fsd_remove_cb) 615 * won't try to remove entries from fsd_list. 616 */ 617 fsd_detaching = 1; 618 while ((fsdi = list_remove_head(&fsd_list)) != NULL) { 619 if (fsdi->fsdi_doomed == 0) { 620 fsdi->fsdi_doomed = 1; 621 622 mutex_enter(&fsd_rem_thread_lock); 623 fsd_rem_thread = curthread; 624 mutex_exit(&fsd_rem_thread_lock); 625 626 /* 627 * fsd_lock is held, so no other thread could have 628 * removed this hook. 629 */ 630 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0); 631 632 mutex_enter(&fsd_rem_thread_lock); 633 fsd_rem_thread = NULL; 634 mutex_exit(&fsd_rem_thread_lock); 635 } 636 } 637 638 while (fsd_list_count > 0) 639 cv_wait(&fsd_cv_empty, &fsd_lock); 640 mutex_exit(&fsd_lock); 641 cv_destroy(&fsd_cv_empty); 642 643 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0); 644 if (fsd_omni_param != NULL) { 645 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 646 fsd_omni_param = NULL; 647 } 648 649 /* After removing the callback and hooks, it is safe to remove these */ 650 list_destroy(&fsd_list); 651 mutex_destroy(&fsd_rem_thread_lock); 652 mutex_destroy(&fsd_lock); 653 654 return (DDI_SUCCESS); 655 } 656 657 static int 658 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 659 { 660 _NOTE(ARGUNUSED(dip)); 661 662 switch (infocmd) { 663 case DDI_INFO_DEVT2DEVINFO: 664 *resultp = fsd_devi; 665 return (DDI_SUCCESS); 666 case DDI_INFO_DEVT2INSTANCE: 667 *resultp = (void *)(uintptr_t)getminor((dev_t)arg); 668 return (DDI_SUCCESS); 669 default: 670 return (DDI_FAILURE); 671 } 672 } 673 674 static int 675 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp) 676 { 677 _NOTE(ARGUNUSED(devp)); 678 679 if (flag & FEXCL || flag & FNDELAY) 680 return (EINVAL); 681 682 if (otyp != OTYP_CHR) 683 return (EINVAL); 684 685 if (!(flag & FREAD && flag & FWRITE)) 686 return (EINVAL); 687 688 if (drv_priv(credp) == EPERM) 689 return (EPERM); 690 691 return (0); 692 } 693 694 static int 695 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp) 696 { 697 _NOTE(ARGUNUSED(dev)); 698 _NOTE(ARGUNUSED(flag)); 699 _NOTE(ARGUNUSED(otyp)); 700 _NOTE(ARGUNUSED(credp)); 701 702 return (0); 703 } 704 705 706 /* ioctl(9E) and it's support functions */ 707 static int 708 fsd_check_param(fsd_t *fsd) 709 { 710 if (fsd->read_less_chance > 100 || 711 fsd->read_less_r[0] > fsd->read_less_r[1]) 712 return (EINVAL); 713 return (0); 714 } 715 716 static int 717 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp) 718 { 719 file_t *file; 720 fsd_dis_t dis; 721 int rv; 722 723 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode)) 724 return (EFAULT); 725 726 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) { 727 *rvalp = rv; 728 return (0); 729 } 730 731 if ((file = getf((int)dis.fsdd_mnt)) == NULL) { 732 *rvalp = EBADFD; 733 return (0); 734 } 735 736 mutex_enter(&fsd_lock); 737 rv = fsd_disturber_install(file->f_vnode->v_vfsp, &dis.fsdd_param); 738 mutex_exit(&fsd_lock); 739 740 releasef((int)dis.fsdd_mnt); 741 742 if (rv != 0) 743 *rvalp = EAGAIN; 744 else 745 *rvalp = 0; 746 747 return (0); 748 } 749 750 static int 751 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp) 752 { 753 file_t *file; 754 fsd_int_t *fsdi; 755 int error = 0; 756 int64_t fd; 757 vfs_t *vfsp; 758 759 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 760 return (EFAULT); 761 762 if ((file = getf((int)fd)) == NULL) { 763 *rvalp = EBADFD; 764 return (0); 765 } 766 vfsp = file->f_vnode->v_vfsp; 767 releasef((int)fd); 768 769 770 mutex_enter(&fsd_lock); 771 772 for (fsdi = list_head(&fsd_list); fsdi != NULL; 773 fsdi = list_next(&fsd_list, fsdi)) { 774 if (fsdi->fsdi_vfsp == vfsp) 775 break; 776 } 777 if (fsdi == NULL) { 778 *rvalp = ENOENT; 779 mutex_exit(&fsd_lock); 780 return (0); 781 } 782 rw_enter(&fsdi->fsdi_lock, RW_READER); 783 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param, 784 sizeof (fsdi->fsdi_param), mode); 785 rw_exit(&fsdi->fsdi_lock); 786 787 mutex_exit(&fsd_lock); 788 789 790 if (error) { 791 return (EFAULT); 792 } else { 793 *rvalp = 0; 794 return (0); 795 } 796 } 797 798 static int 799 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp) 800 { 801 fsd_info_t info; 802 803 mutex_enter(&fsd_lock); 804 info.fsdinf_enabled = fsd_enabled; 805 info.fsdinf_count = fsd_list_count; 806 info.fsdinf_omni_on = fsd_omni_param != NULL; 807 if (info.fsdinf_omni_on) 808 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param, 809 sizeof (info.fsdinf_omni_param)); 810 mutex_exit(&fsd_lock); 811 812 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode)) 813 return (EFAULT); 814 815 *rvalp = 0; 816 return (0); 817 } 818 819 static int 820 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp) 821 { 822 fsd_int_t *fsdi; 823 fsd_fs_t *fsdfs_list; 824 int i; 825 int ret = 0; 826 int64_t ioc_list_count; 827 828 *rvalp = 0; 829 830 /* Get data */ 831 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count, 832 sizeof (ioc_list_count), mode)) 833 return (EFAULT); 834 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list, 835 sizeof (fsdfs_list), mode)) 836 return (EFAULT); 837 838 839 mutex_enter(&fsd_lock); 840 if (ioc_list_count > fsd_list_count) 841 ioc_list_count = fsd_list_count; 842 843 /* Copyout */ 844 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count, 845 sizeof (ioc_list_count), mode)) { 846 ret = EFAULT; 847 goto out; 848 } 849 for (fsdi = list_head(&fsd_list), i = 0; 850 fsdi != NULL && i < ioc_list_count; 851 fsdi = list_next(&fsd_list, fsdi), i++) { 852 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp); 853 int len = strlen(refstr_value(mntstr)); 854 855 rw_enter(&fsdi->fsdi_lock, RW_READER); 856 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name, 857 len + 1, mode) || 858 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param, 859 sizeof (fsdi->fsdi_param), mode)) { 860 ret = EFAULT; 861 } 862 rw_exit(&fsdi->fsdi_lock); 863 refstr_rele(mntstr); 864 865 if (ret != 0) 866 break; 867 } 868 869 870 out: 871 mutex_exit(&fsd_lock); 872 return (ret); 873 } 874 875 static int 876 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp) 877 { 878 file_t *file; 879 int64_t fd; 880 881 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode)) 882 return (EFAULT); 883 884 if ((file = getf((int)fd)) == NULL) { 885 *rvalp = EBADFD; 886 return (0); 887 } 888 889 mutex_enter(&fsd_lock); 890 *rvalp = fsd_disturber_remove(file->f_vnode->v_vfsp); 891 releasef((int)fd); 892 mutex_exit(&fsd_lock); 893 894 return (0); 895 } 896 897 static int 898 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp) 899 { 900 fsd_t fsd; 901 int rv; 902 903 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode)) 904 return (EFAULT); 905 906 if ((rv = fsd_check_param(&fsd)) != 0) { 907 *rvalp = rv; 908 return (0); 909 } 910 911 mutex_enter(&fsd_lock); 912 if (fsd_omni_param == NULL) 913 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param), 914 KM_SLEEP); 915 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param)); 916 mutex_exit(&fsd_lock); 917 918 *rvalp = 0; 919 return (0); 920 } 921 922 923 static int 924 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 925 int *rvalp) 926 { 927 _NOTE(ARGUNUSED(dev)); 928 _NOTE(ARGUNUSED(credp)); 929 930 int enabled; 931 932 mutex_enter(&fsd_lock); 933 enabled = fsd_enabled; 934 mutex_exit(&fsd_lock); 935 936 if (!enabled && cmd != FSD_ENABLE) { 937 *rvalp = ENOTACTIVE; 938 return (0); 939 } 940 941 switch (cmd) { 942 case FSD_ENABLE: 943 fsd_enable(); 944 *rvalp = 0; 945 return (0); 946 947 case FSD_DISABLE: 948 fsd_disable(); 949 *rvalp = 0; 950 return (0); 951 952 case FSD_GET_PARAM: 953 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp)); 954 955 case FSD_DISTURB: 956 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp)); 957 958 case FSD_DISTURB_OFF: 959 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp)); 960 961 case FSD_DISTURB_OMNI: 962 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp)); 963 964 case FSD_DISTURB_OMNI_OFF: 965 mutex_enter(&fsd_lock); 966 if (fsd_omni_param != NULL) 967 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param)); 968 fsd_omni_param = NULL; 969 mutex_exit(&fsd_lock); 970 971 *rvalp = 0; 972 return (0); 973 974 case FSD_GET_LIST: 975 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp)); 976 977 case FSD_GET_INFO: 978 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp)); 979 980 default: 981 return (ENOTTY); 982 } 983 } 984 985 static struct cb_ops cb_ops = { 986 fsd_open, /* open(9E) */ 987 fsd_close, /* close(9E) */ 988 nodev, /* strategy(9E) */ 989 nodev, /* print(9E) */ 990 nodev, /* dump(9E) */ 991 nodev, /* read(9E) */ 992 nodev, /* write(9E) */ 993 fsd_ioctl, /* ioctl(9E) */ 994 nodev, /* devmap(9E) */ 995 nodev, /* mmap(9E) */ 996 nodev, /* segmap(9E) */ 997 nochpoll, /* chpoll(9E) */ 998 ddi_prop_op, /* prop_op(9E) */ 999 NULL, /* streamtab(9E) */ 1000 D_MP | D_64BIT, /* cb_flag(9E) */ 1001 CB_REV, /* cb_rev(9E) */ 1002 nodev, /* aread(9E) */ 1003 nodev, /* awrite(9E) */ 1004 }; 1005 1006 static struct dev_ops dev_ops = { 1007 DEVO_REV, /* driver build version */ 1008 0, /* reference count */ 1009 fsd_getinfo, /* getinfo */ 1010 nulldev, 1011 nulldev, /* probe */ 1012 fsd_attach, /* attach */ 1013 fsd_detach, /* detach */ 1014 nodev, 1015 &cb_ops, /* cb_ops */ 1016 NULL, /* bus_ops */ 1017 NULL, /* power */ 1018 ddi_quiesce_not_needed, /* quiesce */ 1019 }; 1020 1021 static struct modldrv modldrv = { 1022 &mod_driverops, "Filesystem disturber", &dev_ops 1023 }; 1024 1025 static struct modlinkage modlinkage = { 1026 MODREV_1, &modldrv, NULL 1027 }; 1028 1029 int 1030 _init(void) 1031 { 1032 return (mod_install(&modlinkage)); 1033 } 1034 1035 int 1036 _info(struct modinfo *modinfop) 1037 { 1038 return (mod_info(&modlinkage, modinfop)); 1039 } 1040 1041 int 1042 _fini(void) 1043 { 1044 return (mod_remove(&modlinkage)); 1045 }