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 }