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 }