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 }