Update from fsd_sep3 webrev to fsd_sep9
1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 Damian Bogel. All rights reserved.
14 */
15
16 /*
17 * Filesystem disturber pseudo-device driver.
18 */
19
20 #include <sys/conf.h>
21 #include <sys/ddi.h>
22 #include <sys/file.h>
23 #include <sys/fsd.h>
24 #include <sys/fsh.h>
25 #include <sys/kmem.h>
26 #include <sys/ksynch.h>
27 #include <sys/list.h>
28 #include <sys/mkdev.h>
29 #include <sys/refstr.h>
30 #include <sys/stat.h>
31 #include <sys/sunddi.h>
32 #include <sys/sysmacros.h>
33 #include <sys/types.h>
34
35 /*
36 * TODO:
37 * - add checking if a file descriptor passed by the client is indeed
38 * a mountpoint (we'd like to avoid disturbing / instead of an
39 * unmounted filesystem)
40 */
41 /*
42 * fsd - filesystem disturber
43 *
44 * 1. Abstract
45 * Filesystem disturber is a pseudo-device driver used to inject pathological
46 * behaviour into vfs calls. It is NOT a fuzzer. That kind of behaviour
47 * should be expected and correctly handled by software. A simple example of
48 * such behaviour is read() reading less bytes than it was requested. It's
49 * well documented and every read() caller should check the return value of
50 * this function before proceeding.
51 *
52 * 2. Features
53 * * per-vfs injections
54 * * injection installing on every newly mounted vfs (that's called an
55 * omnipresent disturber)
56 *
57 * 3. Usage
58 * fsd_t is a structure which contains all the parameters for the disturbers.
59 * This structure is shared by all hooks on a vfs_t.
60 *
61 * fsd_info_t is filled out by a call to ioctl() and it provides basic
62 * information about fsd's current status.
63 *
64 * fsd_dis_t is passed to ioctl() when a request to disturb a filesystem is
65 * made. It's just a descriptor of a representative file and an fsd_t structure.
66 *
67 * fsd_fs_t is a structure filled out by ioctl() call when the client requests a
68 * full list of disturbers installed in the system.
69 *
70 * fsd_ioc_t is an union for different ioctl() commands.
71 *
72 * ioctl() commands:
73 * FSD_ENABLE:
74 * ioctl(fd, FSD_ENABLE);
75 * Enables the fsd. When fsd is enabled, any attemps to detach the driver
76 * will fail.
77 *
78 * FSD_DISABLE:
79 * ioctl(fd, FSD_DISABLE);
80 * Disables the fsd.
81 *
82 * FSD_GET_PARAM:
83 * ioctl(fd, FSD_GET_PARAM, ioc);
84 * Get's fsd_t associated with a given filesystem. ioc is fsdioc_mnt when
85 * passed to ioctl(). fsdioc_param is the output.
86 * Errors:
87 * ENOENT - the filesystem is not being disturbed
88 *
89 * FSD_DISTURB:
90 * ioctl(fd, FSD_DISTURB, ioc);
91 * Installs a disturber on a given filesystem. If a disturber is already
92 * installed on this filesystem, it overwrites it. ioc is fsdioc_dis.
93 * Errors:
94 * EAGAIN - hook limit exceeded
95 * EBADFD - cannot open the file descriptor
96 * EINVAL - parameters are invalid
97 *
98 * FSD_DISTURB_OFF:
99 * ioctl(fd, FSD_DISTURB_OFF, ioc);
100 * Removes a disturber from a given filesystem. ioc is fsdioc_mnt
101 * Errors:
102 * EBADFD - cannot open the file descriptor
103 * ENOENT - the filesystem is not being disturbed
104 *
105 * FSD_DISTURB_OMNI:
106 * ioctl(fd, FSD_DISTURB_OMNI, ioc);
107 * Install an omnipresent disturber. It means that whenever a new vfs_t is
108 * being created, this disturber is installed on it. If an omnipresent
109 * disturber is already installed, it overwrites it. ioc is fsdioc_param
110 * Errors:
111 * EINVAL - parameters are invalid
112 *
113 * FSD_DISTURB_OMNI_OFF:
114 * ioctl(fd, FSD_DISTURB_OMNI_OFF);
115 * Removes the omnipresent disturber. That does NOT mean that filesystems
116 * which are disturbed because of the omnipresent disturber presence in the
117 * past are going to stop being disturbed after this call.
118 *
119 * FSD_GET_LIST:
120 * ioctl(fd, FSD_GET_LIST, ioc);
121 * Get's a full list of disturbers installed in the system. ioc is
122 * fsdioc_list here. This is a structure with two fields, count and listp.
123 * The count is the number of fsd_fs_t's allocated on the address that
124 * listp is pointing to. There would be at most count fsd_fs_t entries
125 * copied out to the caller. Also, count is set to the number of entries
126 * copied out.
127 *
128 * FSD_GET_INFO:
129 * ioctl(fd, FSD_GET_INFO, ioc);
130 * Get's current information about fsd. ioc is fsdioc_info here.
131 *
132 * At most one hook is installed per vfs_t, and fsd_t describes all possible
133 * disturbance methods. Multiple commands using the fsd should somehow cooperate
134 * in order not to destroy each other efforts in installing disturbers.
135 *
136 * 4. Internals
137 * When fsd_enabled is nonzero, fsd_detach() fails.
138 *
139 * These mount callback is used for installing injections on newly mounted
140 * vfs_t's (omnipresent).
141 *
142 * The list of currently installed hooks is kept in fsd_list.
143 *
144 * fsd installs at most one hook on a vfs_t.
145 *
146 * Inside fsd_detach, we go through fsd_hooks list. There is no guarantee that
147 * a hook remove callback (fsd_remove_cb) wouldn't execute inside
148 * fsh_hook_remove(), thus we can't assume that while walking through fsd_hooks,
149 * our iterator will be valid, because fsh_hook_remove() could invalidate it.
150 * That's why fsd_detaching flag is introduced.
151 *
152 * 5. Locking
153 * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is
154 * protected by fsd_lock.
155 *
156 * Hooks use only the elements of fsd_list, nothing else. Before an element of
157 * fsd_list is destroyed, a hook which uses it is removed. Elements from
158 * fsd_lists are removed and destroyed in the hook remove callback
159 * (fsd_remove_cb).
160 *
161 * Because of the fact that fsd_remove_cb() could be called both in the context
162 * of the thread that executes fsh_hook_remove() or outside the fsd, we need to
163 * use fsd_rem_thread in order not to cause a deadlock. fsh_hook_remove() could
164 * be called by at most one thread inside fsd (fsd_remove_disturber() holds
165 * fsd_lock). We just have to check inside fsd_remove_cb() if it was called
166 * from fsh_hook_remove() or not. We use fsd_rem_thread to determine that.
167 *
168 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock.
169 */
170
171 /*
172 * Once a set of hooks is installed on a filesystem, there's no need
173 * to bother fsh if we want to change the parameters of disturbance.
174 * Intead, we use fsd_lock to protect the fsd_int_t when it's being
175 * used or changed.
176 */
177 typedef struct fsd_int {
178 krwlock_t fsdi_lock; /* protects fsd_param */
179 fsd_t fsdi_param;
180 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */
181 vfs_t *fsdi_vfsp;
182 int fsdi_doomed;
183 list_node_t fsdi_next;
184 } fsd_int_t;
185
186 static dev_info_t *fsd_devi;
187
188
189 /*
190 * fsd_lock protects: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle,
191 * fsd_detaching
192 */
193 static kmutex_t fsd_lock;
194
195 static kthread_t *fsd_rem_thread;
196 static kmutex_t fsd_rem_thread_lock;
197
198 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */
199 static fsh_callback_handle_t fsd_cb_handle;
200 static int fsd_enabled;
201 static int fsd_detaching;
202
203 /*
204 * List of fsd_int_t. For every vfs_t on which fsd has installed a set of hooks
205 * there exist exactly one fsd_int_t with fsdi_vfsp pointing to this vfs_t.
206 */
207 static list_t fsd_list;
208 static int fsd_list_count;
209 static kcondvar_t fsd_cv_empty;
210
211
212 /*
213 * Although it's safe to use this kind of pseudo-random number generator,
214 * it behaves very regular when it comes to parity. Every fsd_rand() call
215 * changes the parity of the seed. That's why when a range of width 2 is set
216 * as a parameter, it's highly possible that the random value will always be
217 * the same, because fsd_rand() could be called the same number of times in a
218 * hook.
219 */
220 static long fsd_rand_seed;
221
222 static int
223 fsd_rand()
224 {
225 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
226 return (fsd_rand_seed & 0x7ffffffff);
227 }
228
229 /* vnode hooks */
230 /*
231 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx()
232 * call, because it's valid until the hooks associated with it are removed.
233 * If a hook is removed, it cannot be executing.
234 */
235 static int
236 fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop,
237 int ioflag, cred_t *cr, caller_context_t *ct)
238 {
239 fsd_int_t *fsdi = (fsd_int_t *)arg;
240 uint64_t count, less, less_chance;
241
242 /*
243 * It is used to keep an odd number of fsd_rand() calls in every
244 * fsd_hook_read() call. That is desired because when a range of width
245 * 2 is set as a parameter, we don't want to make it a constant.
246 * The pseudo-random number generator returns a number with different
247 * parity with every call. If this function is called in every
248 * fsd_hook_read() execution even number of times, it would always be
249 * the same % 2.
250 */
251 (void) fsd_rand();
252
253 ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp);
254
255 rw_enter(&fsdi->fsdi_lock, RW_READER);
256 less_chance = fsdi->fsdi_param.read_less_chance;
257 less = (uint64_t)fsd_rand() %
258 (fsdi->fsdi_param.read_less_r[1] + 1 -
259 fsdi->fsdi_param.read_less_r[0]) + fsdi->fsdi_param.read_less_r[0];
260 rw_exit(&fsdi->fsdi_lock);
261
262 count = uiop->uio_iov->iov_len;
263 if ((uint64_t)fsd_rand() % 100 < less_chance) {
264 extern size_t copyout_max_cached;
265 int ret;
266
267 if (count > less)
268 count -= less;
269 else
270 less = 0;
271
272 uiop->uio_iov->iov_len = count;
273 uiop->uio_resid = count;
274 if (count <= copyout_max_cached)
275 uiop->uio_extflg = UIO_COPY_CACHED;
276 else
277 uiop->uio_extflg = UIO_COPY_DEFAULT;
278
279 ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct);
280 uiop->uio_resid += less;
281 return (ret);
282 }
283
284 return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct));
285 }
286
287
288 static void
289 fsd_remove_cb(void *arg, fsh_handle_t handle)
290 {
291 _NOTE(ARGUNUSED(handle));
292
293 fsd_int_t *fsdi = (fsd_int_t *)arg;
294 int fsd_context;
295
296 mutex_enter(&fsd_rem_thread_lock);
297 fsd_context = fsd_rem_thread == curthread;
298 mutex_exit(&fsd_rem_thread_lock);
299
300 if (!fsd_context)
301 mutex_enter(&fsd_lock);
302
303 ASSERT(MUTEX_HELD(&fsd_lock));
304
305 if (!fsd_detaching)
306 list_remove(&fsd_list, fsdi);
307
308 rw_destroy(&fsdi->fsdi_lock);
309 kmem_free(fsdi, sizeof (*fsdi));
310
311 fsd_list_count--;
312 if (fsd_list_count == 0)
313 cv_signal(&fsd_cv_empty);
314
315 if (!fsd_context)
316 mutex_exit(&fsd_lock);
317 }
318
319 /*
320 * Installs a set of hook with given parameters on a vfs_t.
321 *
322 * It is expected that fsd_lock is being held.
323 *
324 * Returns 0 on success and non-zero if hook limit exceeded.
325 */
326 static int
327 fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd)
328 {
329 fsd_int_t *fsdi;
330
331 ASSERT(MUTEX_HELD(&fsd_lock));
332
333 for (fsdi = list_head(&fsd_list); fsdi != NULL;
334 fsdi = list_next(&fsd_list, fsdi)) {
335 if (fsdi->fsdi_vfsp == vfsp)
336 break;
337 }
338
339 if (fsdi != NULL) {
340 /* Just change the existing fsd_int_t */
341 rw_enter(&fsdi->fsdi_lock, RW_WRITER);
342 (void) memcpy(&fsdi->fsdi_param, fsd,
343 sizeof (fsdi->fsdi_param));
344 rw_exit(&fsdi->fsdi_lock);
345 } else {
346 fsh_t hook = { 0 };
347
348 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
349 fsdi->fsdi_vfsp = vfsp;
350 (void) memcpy(&fsdi->fsdi_param, fsd,
351 sizeof (fsdi->fsdi_param));
352 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
353
354 hook.arg = fsdi;
355 hook.read = fsd_hook_read;
356 hook.remove_cb = fsd_remove_cb;
357
358 /*
359 * It is safe to do so, because none of the hooks installed
360 * by fsd uses fsdi_handle nor the fsd_list.
361 */
362 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
363 if (fsdi->fsdi_handle == -1) {
364 kmem_free(fsdi, sizeof (*fsdi));
365 rw_destroy(&fsdi->fsdi_lock);
366 return (-1);
367 }
368 list_insert_head(&fsd_list, fsdi);
369 fsd_list_count++;
370 }
371 return (0);
372 }
373
374 static int
375 fsd_remove_disturber(vfs_t *vfsp)
376 {
377 fsd_int_t *fsdi;
378
379 ASSERT(MUTEX_HELD(&fsd_lock));
380
381 for (fsdi = list_head(&fsd_list); fsdi != NULL;
382 fsdi = list_next(&fsd_list, fsdi)) {
383 if (fsdi->fsdi_vfsp == vfsp)
384 break;
385 }
386 if (fsdi == NULL || fsdi->fsdi_doomed)
387 return (ENOENT);
388
389 fsdi->fsdi_doomed = 1;
390
391 mutex_enter(&fsd_rem_thread_lock);
392 fsd_rem_thread = curthread;
393 mutex_exit(&fsd_rem_thread_lock);
394
395 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
396
397 mutex_enter(&fsd_rem_thread_lock);
398 fsd_rem_thread = NULL;
399 mutex_exit(&fsd_rem_thread_lock);
400
401 return (0);
402 }
403
404 static void
405 fsd_callback_mount(vfs_t *vfsp, void *arg)
406 {
407 _NOTE(ARGUNUSED(arg));
408
409 int error = 0;
410
411 mutex_enter(&fsd_lock);
412 if (fsd_omni_param != NULL)
413 error = fsd_install_disturber(vfsp, fsd_omni_param);
414 mutex_exit(&fsd_lock);
415
416 if (error != 0) {
417 refstr_t *mntref;
418
419 mntref = vfs_getmntpoint(vfsp);
420 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
421 refstr_value(mntref));
422 refstr_rele(mntref);
423 }
424 }
425
426 static void
427 fsd_enable()
428 {
429 mutex_enter(&fsd_lock);
430 fsd_enabled = 1;
431 mutex_exit(&fsd_lock);
432 }
433
434 static void
435 fsd_disable()
436 {
437 mutex_enter(&fsd_lock);
438 fsd_enabled = 0;
439 mutex_exit(&fsd_lock);
440 }
441
442
443 /* Entry points */
444 static int
445 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
446 {
447 minor_t instance;
448 fsh_callback_t cb = { 0 };
449
450 if (cmd != DDI_ATTACH)
451 return (DDI_FAILURE);
452
453 if (fsd_devi != NULL)
454 return (DDI_FAILURE);
455
456 instance = ddi_get_instance(dip);
457 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
458 DDI_PSEUDO, 0) == DDI_FAILURE)
459 return (DDI_FAILURE);
460 fsd_devi = dip;
461 ddi_report_dev(fsd_devi);
462
463 list_create(&fsd_list, sizeof (fsd_int_t),
464 offsetof(fsd_int_t, fsdi_next));
465
466 fsd_rand_seed = gethrtime();
467
468 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
469 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
470 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
471
472 cb.fshc_mount = fsd_callback_mount;
473 cb.fshc_arg = fsd_omni_param;
474 fsd_cb_handle = fsh_callback_install(&cb);
475 if (fsd_cb_handle == -1) {
476 /* Cleanup */
477 list_destroy(&fsd_list);
478 cv_destroy(&fsd_cv_empty);
479 mutex_destroy(&fsd_rem_thread_lock);
480 mutex_destroy(&fsd_lock);
481 ddi_remove_minor_node(fsd_devi, NULL);
482 fsd_devi = NULL;
483 return (DDI_FAILURE);
484 }
485
486 return (DDI_SUCCESS);
487 }
488
489 /*
490 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
491 * detach will fail.
492 */
493 static int
494 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
495 {
496 fsd_int_t *fsdi;
497
498 if (cmd != DDI_DETACH)
499 return (DDI_FAILURE);
500
501 ASSERT(dip == fsd_devi);
502
503 /*
504 * No need to hold fsd_lock here. Since only the hooks and callbacks
505 * might be running at this point.
506 */
507 if (fsd_enabled)
508 return (DDI_FAILURE);
509
510 ddi_remove_minor_node(dip, NULL);
511 fsd_devi = NULL;
512
513 mutex_enter(&fsd_lock);
514 fsd_detaching = 1;
515 while ((fsdi = list_remove_head(&fsd_list)) != NULL)
516 if (fsdi->fsdi_doomed == 0) {
517 fsdi->fsdi_doomed = 1;
518
519 mutex_enter(&fsd_rem_thread_lock);
520 fsd_rem_thread = curthread;
521 mutex_exit(&fsd_rem_thread_lock);
522
523 /*
524 * fsd_lock is held, so no other thread could have
525 * removed this hook.
526 */
527 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
528
529 mutex_enter(&fsd_rem_thread_lock);
530 fsd_rem_thread = NULL;
531 mutex_exit(&fsd_rem_thread_lock);
532 }
533
534 while (fsd_list_count > 0)
535 cv_wait(&fsd_cv_empty, &fsd_lock);
536 mutex_exit(&fsd_lock);
537 cv_destroy(&fsd_cv_empty);
538
539 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
540 if (fsd_omni_param != NULL) {
541 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
542 fsd_omni_param = NULL;
543 }
544
545 /* After removing the callback and hooks, it is safe to remove these */
546 list_destroy(&fsd_list);
547 mutex_destroy(&fsd_rem_thread_lock);
548 mutex_destroy(&fsd_lock);
549
550 return (DDI_SUCCESS);
551 }
552
553 static int
554 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
555 {
556 _NOTE(ARGUNUSED(dip));
557
558 switch (infocmd) {
559 case DDI_INFO_DEVT2DEVINFO:
560 *resultp = fsd_devi;
561 return (DDI_SUCCESS);
562 case DDI_INFO_DEVT2INSTANCE:
563 *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
564 return (DDI_SUCCESS);
565 default:
566 return (DDI_FAILURE);
567 }
568 }
569
570 static int
571 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
572 {
573 _NOTE(ARGUNUSED(devp));
574
575 if (flag & FEXCL || flag & FNDELAY)
576 return (EINVAL);
577
578 if (otyp != OTYP_CHR)
579 return (EINVAL);
580
581 if (!(flag & FREAD && flag & FWRITE))
582 return (EINVAL);
583
584 if (drv_priv(credp) == EPERM)
585 return (EPERM);
586
587 return (0);
588 }
589
590 static int
591 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp)
592 {
593 _NOTE(ARGUNUSED(dev));
594 _NOTE(ARGUNUSED(flag));
595 _NOTE(ARGUNUSED(otyp));
596 _NOTE(ARGUNUSED(credp));
597
598 return (0);
599 }
600
601
602 /* ioctl(9E) and it's support functions */
603 static int
604 fsd_check_param(fsd_t *fsd)
605 {
606 if (fsd->read_less_chance > 100 ||
607 fsd->read_less_r[0] > fsd->read_less_r[1])
608 return (EINVAL);
609 return (0);
610 }
611
612 static int
613 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
614 {
615 file_t *file;
616 fsd_dis_t dis;
617 int rv;
618
619 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
620 return (EFAULT);
621
622 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
623 *rvalp = rv;
624 return (0);
625 }
626
627 if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
628 *rvalp = EBADFD;
629 return (0);
630 }
631
632 mutex_enter(&fsd_lock);
633 rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param);
634 mutex_exit(&fsd_lock);
635
636 releasef((int)dis.fsdd_mnt);
637
638 if (rv != 0)
639 *rvalp = EAGAIN;
640 else
641 *rvalp = 0;
642
643 return (0);
644 }
645
646 static int
647 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
648 {
649 file_t *file;
650 fsd_int_t *fsdi;
651 int error = 0;
652 int64_t fd;
653 vfs_t *vfsp;
654
655 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
656 return (EFAULT);
657
658 if ((file = getf((int)fd)) == NULL) {
659 *rvalp = EBADFD;
660 return (0);
661 }
662 vfsp = file->f_vnode->v_vfsp;
663 releasef((int)fd);
664
665
666 mutex_enter(&fsd_lock);
667
668 for (fsdi = list_head(&fsd_list); fsdi != NULL;
669 fsdi = list_next(&fsd_list, fsdi)) {
670 if (fsdi->fsdi_vfsp == vfsp)
671 break;
672 }
673 if (fsdi == NULL) {
674 *rvalp = ENOENT;
675 mutex_exit(&fsd_lock);
676 return (0);
677 }
678 rw_enter(&fsdi->fsdi_lock, RW_READER);
679 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param,
680 sizeof (fsdi->fsdi_param), mode);
681 rw_exit(&fsdi->fsdi_lock);
682
683 mutex_exit(&fsd_lock);
684
685
686 if (error) {
687 return (EFAULT);
688 } else {
689 *rvalp = 0;
690 return (0);
691 }
692 }
693
694 static int
695 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp)
696 {
697 fsd_info_t info;
698
699 mutex_enter(&fsd_lock);
700 info.fsdinf_enabled = fsd_enabled;
701 info.fsdinf_count = fsd_list_count;
702 info.fsdinf_omni_on = fsd_omni_param != NULL;
703 if (info.fsdinf_omni_on)
704 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param,
705 sizeof (info.fsdinf_omni_param));
706 mutex_exit(&fsd_lock);
707
708 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode))
709 return (EFAULT);
710
711 *rvalp = 0;
712 return (0);
713 }
714
715 static int
716 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp)
717 {
718 fsd_int_t *fsdi;
719 fsd_fs_t *fsdfs_list;
720 int i;
721 int ret = 0;
722 int64_t ioc_list_count;
723
724 *rvalp = 0;
725
726 /* Get data */
727 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count,
728 sizeof (ioc_list_count), mode))
729 return (EFAULT);
730 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list,
731 sizeof (fsdfs_list), mode))
732 return (EFAULT);
733
734
735 mutex_enter(&fsd_lock);
736 if (ioc_list_count > fsd_list_count)
737 ioc_list_count = fsd_list_count;
738
739 /* Copyout */
740 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count,
741 sizeof (ioc_list_count), mode)) {
742 ret = EFAULT;
743 goto out;
744 }
745 for (fsdi = list_head(&fsd_list), i = 0;
746 fsdi != NULL && i < ioc_list_count;
747 fsdi = list_next(&fsd_list, fsdi), i++) {
748 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp);
749 int len = strlen(refstr_value(mntstr));
750
751 rw_enter(&fsdi->fsdi_lock, RW_READER);
752 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name,
753 len + 1, mode) ||
754 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param,
755 sizeof (fsdi->fsdi_param), mode)) {
756 ret = EFAULT;
757 }
758 rw_exit(&fsdi->fsdi_lock);
759 refstr_rele(mntstr);
760
761 if (ret != 0)
762 break;
763 }
764
765
766 out:
767 mutex_exit(&fsd_lock);
768 return (ret);
769 }
770
771 static int
772 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
773 {
774 file_t *file;
775 int64_t fd;
776
777 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
778 return (EFAULT);
779
780 if ((file = getf((int)fd)) == NULL) {
781 *rvalp = EBADFD;
782 return (0);
783 }
784
785 mutex_enter(&fsd_lock);
786 *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp);
787 releasef((int)fd);
788 mutex_exit(&fsd_lock);
789
790 return (0);
791 }
792
793 static int
794 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
795 {
796 fsd_t fsd;
797 int rv;
798
799 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
800 return (EFAULT);
801
802 if ((rv = fsd_check_param(&fsd)) != 0) {
803 *rvalp = rv;
804 return (0);
805 }
806
807 mutex_enter(&fsd_lock);
808 if (fsd_omni_param == NULL)
809 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
810 KM_SLEEP);
811 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
812 mutex_exit(&fsd_lock);
813
814 *rvalp = 0;
815 return (0);
816 }
817
818
819 static int
820 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
821 int *rvalp)
822 {
823 _NOTE(ARGUNUSED(dev));
824 _NOTE(ARGUNUSED(credp));
825
826 if (!fsd_enabled && cmd != FSD_ENABLE) {
827 *rvalp = ENOTACTIVE;
828 return (0);
829 }
830
831 switch (cmd) {
832 case FSD_ENABLE:
833 fsd_enable();
834 *rvalp = 0;
835 return (0);
836
837 case FSD_DISABLE:
838 fsd_disable();
839 *rvalp = 0;
840 return (0);
841
842 case FSD_GET_PARAM:
843 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
844
845 case FSD_DISTURB:
846 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
847
848 case FSD_DISTURB_OFF:
849 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp));
850
851 case FSD_DISTURB_OMNI:
852 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp));
853
854 case FSD_DISTURB_OMNI_OFF:
855 mutex_enter(&fsd_lock);
856 if (fsd_omni_param != NULL)
857 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
858 fsd_omni_param = NULL;
859 mutex_exit(&fsd_lock);
860
861 *rvalp = 0;
862 return (0);
863
864 case FSD_GET_LIST:
865 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp));
866
867 case FSD_GET_INFO:
868 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp));
869
870 default:
871 return (ENOTTY);
872 }
873 }
874
875 static struct cb_ops cb_ops = {
876 fsd_open, /* open(9E) */
877 fsd_close, /* close(9E) */
878 nodev, /* strategy(9E) */
879 nodev, /* print(9E) */
880 nodev, /* dump(9E) */
881 nodev, /* read(9E) */
882 nodev, /* write(9E) */
883 fsd_ioctl, /* ioctl(9E) */
884 nodev, /* devmap(9E) */
885 nodev, /* mmap(9E) */
886 nodev, /* segmap(9E) */
887 nochpoll, /* chpoll(9E) */
888 ddi_prop_op, /* prop_op(9E) */
889 NULL, /* streamtab(9E) */
890 D_MP | D_64BIT, /* cb_flag(9E) */
891 CB_REV, /* cb_rev(9E) */
892 nodev, /* aread(9E) */
893 nodev, /* awrite(9E) */
894 };
895
896 static struct dev_ops dev_ops = {
897 DEVO_REV, /* driver build version */
898 0, /* reference count */
899 fsd_getinfo, /* getinfo */
900 nulldev,
901 nulldev, /* probe */
902 fsd_attach, /* attach */
903 fsd_detach, /* detach */
904 nodev,
905 &cb_ops, /* cb_ops */
906 NULL, /* bus_ops */
907 NULL, /* power */
908 ddi_quiesce_not_needed, /* quiesce */
909 };
910
911 static struct modldrv modldrv = {
912 &mod_driverops, "Filesystem disturber", &dev_ops
913 };
914
915 static struct modlinkage modlinkage = {
916 MODREV_1, &modldrv, NULL
917 };
918
919 int
920 _init(void)
921 {
922 return (mod_install(&modlinkage));
923 }
924
925 int
926 _info(struct modinfo *modinfop)
927 {
928 return (mod_info(&modlinkage, modinfop));
929 }
930
931 int
932 _fini(void)
933 {
934 return (mod_remove(&modlinkage));
935 }
--- EOF ---