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