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 }