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). The free callback is used for cleaning up.
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_disturber_remove() 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_node;
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 void
236 fsd_hook_pre_read(void *arg, void **instancep, vnode_t **vpp, uio_t **uiopp,
237 int *ioflagp, cred_t **crp, caller_context_t **ctp)
238 {
239 _NOTE(ARGUNUSED(ioflagp));
240 _NOTE(ARGUNUSED(crp));
241 _NOTE(ARGUNUSED(ctp));
242
243 fsd_int_t *fsdi = (fsd_int_t *)arg;
244 uint64_t less_chance;
245
246 /*
247 * It is used to keep an odd number of fsd_rand() calls in every
248 * fsd_hook_pre_read() call. That is desired because when a range of
249 * width 2 is set as a parameter, we don't want to make it a constant.
250 * The pseudo-random number generator returns a number with different
251 * parity with every call. If this function is called in every
252 * fsd_hook_pre_read() execution even number of times, it would always
253 * be the same % 2.
254 */
255 (void) fsd_rand();
256
257 ASSERT((*vpp)->v_vfsp == fsdi->fsdi_vfsp);
258
259 rw_enter(&fsdi->fsdi_lock, RW_READER);
260 less_chance = fsdi->fsdi_param.read_less_chance;
261 rw_exit(&fsdi->fsdi_lock);
262
263 if ((uint64_t)fsd_rand() % 100 < less_chance) {
264 extern size_t copyout_max_cached;
265 uint64_t r[2];
266 uint64_t count, less;
267
268 count = (*uiopp)->uio_iov->iov_len;
269 r[0] = fsdi->fsdi_param.read_less_r[0];
270 r[1] = fsdi->fsdi_param.read_less_r[1];
271 less = (uint64_t)fsd_rand() % (r[1] + 1 - r[0]) + r[0];
272
273 if (count > less) {
274 count -= less;
275 *instancep = kmem_alloc(sizeof (uint64_t), KM_SLEEP);
276 *(*(uint64_t **)instancep) = less;
277 } else {
278 *instancep = NULL;
279 return;
280 }
281
282 (*uiopp)->uio_iov->iov_len = count;
283 (*uiopp)->uio_resid = count;
284 if (count <= copyout_max_cached)
285 (*uiopp)->uio_extflg = UIO_COPY_CACHED;
286 else
287 (*uiopp)->uio_extflg = UIO_COPY_DEFAULT;
288 } else {
289 *instancep = NULL;
290 }
291 }
292
293 static int
294 fsd_hook_post_read(int ret, void *arg, void *instance, vnode_t *vp,
295 uio_t *uiop, int oflag, cred_t *cr, caller_context_t *ct)
296 {
297 _NOTE(ARGUNUSED(arg));
298 _NOTE(ARGUNUSED(vp));
299 _NOTE(ARGUNUSED(oflag));
300 _NOTE(ARGUNUSED(cr));
301 _NOTE(ARGUNUSED(ct));
302
303 if (instance != NULL) {
304 uint64_t *lessp = instance;
305 uiop->uio_resid += *lessp;
306 kmem_free(lessp, sizeof (*lessp));
307 }
308 return (ret);
309 }
310
311 static void
312 fsd_remove_cb(void *arg, fsh_handle_t handle)
313 {
314 _NOTE(ARGUNUSED(handle));
315
316 fsd_int_t *fsdi = (fsd_int_t *)arg;
317 int fsd_context;
318
319 mutex_enter(&fsd_rem_thread_lock);
320 fsd_context = fsd_rem_thread == curthread;
321 mutex_exit(&fsd_rem_thread_lock);
322
323 if (!fsd_context)
324 mutex_enter(&fsd_lock);
325
326 ASSERT(MUTEX_HELD(&fsd_lock));
327
328 if (!fsd_detaching)
329 list_remove(&fsd_list, fsdi);
330
331 rw_destroy(&fsdi->fsdi_lock);
332 kmem_free(fsdi, sizeof (*fsdi));
333
334 fsd_list_count--;
335 if (fsd_list_count == 0)
336 cv_signal(&fsd_cv_empty);
337
338 if (!fsd_context)
339 mutex_exit(&fsd_lock);
340 }
341
342 /*
343 * Installs a set of hook with given parameters on a vfs_t.
344 *
345 * It is expected that fsd_lock is being held.
346 *
347 * Returns 0 on success and non-zero if hook limit exceeded.
348 */
349 static int
350 fsd_disturber_install(vfs_t *vfsp, fsd_t *fsd)
351 {
352 fsd_int_t *fsdi;
353
354 ASSERT(MUTEX_HELD(&fsd_lock));
355
356 for (fsdi = list_head(&fsd_list); fsdi != NULL;
357 fsdi = list_next(&fsd_list, fsdi)) {
358 if (fsdi->fsdi_vfsp == vfsp)
359 break;
360 }
361
362 if (fsdi != NULL) {
363 /* Just change the existing fsd_int_t */
364 rw_enter(&fsdi->fsdi_lock, RW_WRITER);
365 (void) memcpy(&fsdi->fsdi_param, fsd,
366 sizeof (fsdi->fsdi_param));
367 rw_exit(&fsdi->fsdi_lock);
368 } else {
369 fsh_t hook = { 0 };
370
371 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
372 fsdi->fsdi_vfsp = vfsp;
373 (void) memcpy(&fsdi->fsdi_param, fsd,
374 sizeof (fsdi->fsdi_param));
375 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
376
377 hook.arg = fsdi;
378 hook.pre_read = fsd_hook_pre_read;
379 hook.post_read = fsd_hook_post_read;
380 hook.remove_cb = fsd_remove_cb;
381
382 /*
383 * It is safe to do so, because none of the hooks installed
384 * by fsd uses fsdi_handle nor the fsd_list.
385 */
386 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
387 if (fsdi->fsdi_handle == -1) {
388 kmem_free(fsdi, sizeof (*fsdi));
389 rw_destroy(&fsdi->fsdi_lock);
390 return (-1);
391 }
392 list_insert_head(&fsd_list, fsdi);
393 fsd_list_count++;
394 }
395 return (0);
396 }
397
398 static int
399 fsd_disturber_remove(vfs_t *vfsp)
400 {
401 fsd_int_t *fsdi;
402
403 ASSERT(MUTEX_HELD(&fsd_lock));
404
405 for (fsdi = list_head(&fsd_list); fsdi != NULL;
406 fsdi = list_next(&fsd_list, fsdi)) {
407 if (fsdi->fsdi_vfsp == vfsp)
408 break;
409 }
410 if (fsdi == NULL || fsdi->fsdi_doomed)
411 return (ENOENT);
412
413 fsdi->fsdi_doomed = 1;
414
415 mutex_enter(&fsd_rem_thread_lock);
416 fsd_rem_thread = curthread;
417 mutex_exit(&fsd_rem_thread_lock);
418
419 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
420
421 mutex_enter(&fsd_rem_thread_lock);
422 fsd_rem_thread = NULL;
423 mutex_exit(&fsd_rem_thread_lock);
424
425 return (0);
426 }
427
428 static void
429 fsd_mount_callback(vfs_t *vfsp, void *arg)
430 {
431 _NOTE(ARGUNUSED(arg));
432
433 int error = 0;
434
435 mutex_enter(&fsd_lock);
436 if (fsd_omni_param != NULL)
437 error = fsd_disturber_install(vfsp, fsd_omni_param);
438 mutex_exit(&fsd_lock);
439
440 if (error != 0) {
441 refstr_t *mntref;
442
443 mntref = vfs_getmntpoint(vfsp);
444 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
445 refstr_value(mntref));
446 refstr_rele(mntref);
447 }
448 }
449
450 /*
451 * Although, we might delete the fsd_free_callback(), it would make the whole
452 * proces less clear. There's a time window between firing free callbacks and
453 * freeing the vfs_t in fsd_disturber_remove() could be called. fsh can
454 * deal with invalid handles (until there is no collision), but we'd like to
455 * have a nice assertion instead.
456 */
457 static void
458 fsd_free_callback(vfs_t *vfsp, void *arg)
459 {
460 _NOTE(ARGUNUSED(arg));
461
462 fsd_int_t *fsdi;
463
464 mutex_enter(&fsd_lock);
465 for (fsdi = list_head(&fsd_list); fsdi != NULL;
466 fsdi = list_next(&fsd_list, fsdi)) {
467 if (fsdi->fsdi_vfsp == vfsp) {
468 if (fsdi->fsdi_doomed)
469 continue;
470
471 fsdi->fsdi_doomed = 1;
472 /*
473 * We make such assertion, because fsd_lock is held
474 * and that means that neither fsd_disturber_remove()
475 * nor fsd_remove_cb() has removed this hook in
476 * different thread.
477 */
478 mutex_enter(&fsd_rem_thread_lock);
479 fsd_rem_thread = curthread;
480 mutex_exit(&fsd_rem_thread_lock);
481
482 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
483
484 mutex_enter(&fsd_rem_thread_lock);
485 fsd_rem_thread = NULL;
486 mutex_exit(&fsd_rem_thread_lock);
487
488 /*
489 * Since there is at most one hook installed by fsd,
490 * we break.
491 */
492 break;
493 }
494 }
495 /*
496 * We can't write ASSERT(fsdi != NULL) because it is possible that
497 * there was a concurrent call to fsd_disturber_remove() or
498 * fsd_detach().
499 */
500 mutex_exit(&fsd_lock);
501 }
502
503 static void
504 fsd_enable()
505 {
506 mutex_enter(&fsd_lock);
507 fsd_enabled = 1;
508 mutex_exit(&fsd_lock);
509 }
510
511 static void
512 fsd_disable()
513 {
514 mutex_enter(&fsd_lock);
515 fsd_enabled = 0;
516 mutex_exit(&fsd_lock);
517 }
518
519
520 /* Entry points */
521 static int
522 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
523 {
524 minor_t instance;
525 fsh_callback_t cb = { 0 };
526
527 if (cmd != DDI_ATTACH)
528 return (DDI_FAILURE);
529
530 if (fsd_devi != NULL)
531 return (DDI_FAILURE);
532
533 instance = ddi_get_instance(dip);
534 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
535 DDI_PSEUDO, 0) == DDI_FAILURE)
536 return (DDI_FAILURE);
537 fsd_devi = dip;
538 ddi_report_dev(fsd_devi);
539
540 list_create(&fsd_list, sizeof (fsd_int_t),
541 offsetof(fsd_int_t, fsdi_node));
542
543 fsd_rand_seed = gethrtime();
544
545 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
546 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
547 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
548
549 cb.fshc_mount = fsd_mount_callback;
550 cb.fshc_free = fsd_free_callback;
551 cb.fshc_arg = fsd_omni_param;
552 fsd_cb_handle = fsh_callback_install(&cb);
553 if (fsd_cb_handle == -1) {
554 /* Cleanup */
555 list_destroy(&fsd_list);
556 cv_destroy(&fsd_cv_empty);
557 mutex_destroy(&fsd_rem_thread_lock);
558 mutex_destroy(&fsd_lock);
559 ddi_remove_minor_node(fsd_devi, NULL);
560 fsd_devi = NULL;
561 return (DDI_FAILURE);
562 }
563
564 return (DDI_SUCCESS);
565 }
566
567 /*
568 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
569 * detach will fail.
570 */
571 static int
572 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
573 {
574 fsd_int_t *fsdi;
575
576 if (cmd != DDI_DETACH)
577 return (DDI_FAILURE);
578
579 ASSERT(dip == fsd_devi);
580
581 /*
582 * No need to hold fsd_lock here. Since only the hooks and callbacks
583 * might be running at this point.
584 */
585 if (fsd_enabled)
586 return (DDI_FAILURE);
587
588 ddi_remove_minor_node(dip, NULL);
589 fsd_devi = NULL;
590
591 /*
592 * 1. Remove the hooks.
593 * 2. Remove the callbacks.
594 *
595 * This order has to be preserved, because of the fact that
596 * fsd_free_callback() is the last stop before a vfs_t is destroyed.
597 * Without it, this might happen:
598 * vfs_free() fsd_detach()
599 * 1. Handle for the hook is
600 * invalidated.
601 * 2. Fired fsd_remove_cb().
602 * 3. fsd_remove_cb() hasn't yet fsd_lock is acquired.
603 * acquired the fsd_lock.
604 * 4 Waiting for fsd_lock. That ASSERT(fsh_hook_remove(..) == 0);
605 * means that the hook hasn't failed, because the handle is
606 * been removed from fsd_hooks already invalid.
607 * fsd_hooks yet.
608 *
609 * The ASSERT() here is nice and without a good reason, we don't want
610 * to get rid of it.
611 */
612 mutex_enter(&fsd_lock);
613 /*
614 * After we set fsd_detaching to 1, hook remove callback (fsd_remove_cb)
615 * won't try to remove entries from fsd_list.
616 */
617 fsd_detaching = 1;
618 while ((fsdi = list_remove_head(&fsd_list)) != NULL) {
619 if (fsdi->fsdi_doomed == 0) {
620 fsdi->fsdi_doomed = 1;
621
622 mutex_enter(&fsd_rem_thread_lock);
623 fsd_rem_thread = curthread;
624 mutex_exit(&fsd_rem_thread_lock);
625
626 /*
627 * fsd_lock is held, so no other thread could have
628 * removed this hook.
629 */
630 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
631
632 mutex_enter(&fsd_rem_thread_lock);
633 fsd_rem_thread = NULL;
634 mutex_exit(&fsd_rem_thread_lock);
635 }
636 }
637
638 while (fsd_list_count > 0)
639 cv_wait(&fsd_cv_empty, &fsd_lock);
640 mutex_exit(&fsd_lock);
641 cv_destroy(&fsd_cv_empty);
642
643 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
644 if (fsd_omni_param != NULL) {
645 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
646 fsd_omni_param = NULL;
647 }
648
649 /* After removing the callback and hooks, it is safe to remove these */
650 list_destroy(&fsd_list);
651 mutex_destroy(&fsd_rem_thread_lock);
652 mutex_destroy(&fsd_lock);
653
654 return (DDI_SUCCESS);
655 }
656
657 static int
658 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
659 {
660 _NOTE(ARGUNUSED(dip));
661
662 switch (infocmd) {
663 case DDI_INFO_DEVT2DEVINFO:
664 *resultp = fsd_devi;
665 return (DDI_SUCCESS);
666 case DDI_INFO_DEVT2INSTANCE:
667 *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
668 return (DDI_SUCCESS);
669 default:
670 return (DDI_FAILURE);
671 }
672 }
673
674 static int
675 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
676 {
677 _NOTE(ARGUNUSED(devp));
678
679 if (flag & FEXCL || flag & FNDELAY)
680 return (EINVAL);
681
682 if (otyp != OTYP_CHR)
683 return (EINVAL);
684
685 if (!(flag & FREAD && flag & FWRITE))
686 return (EINVAL);
687
688 if (drv_priv(credp) == EPERM)
689 return (EPERM);
690
691 return (0);
692 }
693
694 static int
695 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp)
696 {
697 _NOTE(ARGUNUSED(dev));
698 _NOTE(ARGUNUSED(flag));
699 _NOTE(ARGUNUSED(otyp));
700 _NOTE(ARGUNUSED(credp));
701
702 return (0);
703 }
704
705
706 /* ioctl(9E) and it's support functions */
707 static int
708 fsd_check_param(fsd_t *fsd)
709 {
710 if (fsd->read_less_chance > 100 ||
711 fsd->read_less_r[0] > fsd->read_less_r[1])
712 return (EINVAL);
713 return (0);
714 }
715
716 static int
717 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
718 {
719 file_t *file;
720 fsd_dis_t dis;
721 int rv;
722
723 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
724 return (EFAULT);
725
726 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
727 *rvalp = rv;
728 return (0);
729 }
730
731 if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
732 *rvalp = EBADFD;
733 return (0);
734 }
735
736 mutex_enter(&fsd_lock);
737 rv = fsd_disturber_install(file->f_vnode->v_vfsp, &dis.fsdd_param);
738 mutex_exit(&fsd_lock);
739
740 releasef((int)dis.fsdd_mnt);
741
742 if (rv != 0)
743 *rvalp = EAGAIN;
744 else
745 *rvalp = 0;
746
747 return (0);
748 }
749
750 static int
751 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
752 {
753 file_t *file;
754 fsd_int_t *fsdi;
755 int error = 0;
756 int64_t fd;
757 vfs_t *vfsp;
758
759 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
760 return (EFAULT);
761
762 if ((file = getf((int)fd)) == NULL) {
763 *rvalp = EBADFD;
764 return (0);
765 }
766 vfsp = file->f_vnode->v_vfsp;
767 releasef((int)fd);
768
769
770 mutex_enter(&fsd_lock);
771
772 for (fsdi = list_head(&fsd_list); fsdi != NULL;
773 fsdi = list_next(&fsd_list, fsdi)) {
774 if (fsdi->fsdi_vfsp == vfsp)
775 break;
776 }
777 if (fsdi == NULL) {
778 *rvalp = ENOENT;
779 mutex_exit(&fsd_lock);
780 return (0);
781 }
782 rw_enter(&fsdi->fsdi_lock, RW_READER);
783 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param,
784 sizeof (fsdi->fsdi_param), mode);
785 rw_exit(&fsdi->fsdi_lock);
786
787 mutex_exit(&fsd_lock);
788
789
790 if (error) {
791 return (EFAULT);
792 } else {
793 *rvalp = 0;
794 return (0);
795 }
796 }
797
798 static int
799 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp)
800 {
801 fsd_info_t info;
802
803 mutex_enter(&fsd_lock);
804 info.fsdinf_enabled = fsd_enabled;
805 info.fsdinf_count = fsd_list_count;
806 info.fsdinf_omni_on = fsd_omni_param != NULL;
807 if (info.fsdinf_omni_on)
808 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param,
809 sizeof (info.fsdinf_omni_param));
810 mutex_exit(&fsd_lock);
811
812 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode))
813 return (EFAULT);
814
815 *rvalp = 0;
816 return (0);
817 }
818
819 static int
820 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp)
821 {
822 fsd_int_t *fsdi;
823 fsd_fs_t *fsdfs_list;
824 int i;
825 int ret = 0;
826 int64_t ioc_list_count;
827
828 *rvalp = 0;
829
830 /* Get data */
831 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count,
832 sizeof (ioc_list_count), mode))
833 return (EFAULT);
834 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list,
835 sizeof (fsdfs_list), mode))
836 return (EFAULT);
837
838
839 mutex_enter(&fsd_lock);
840 if (ioc_list_count > fsd_list_count)
841 ioc_list_count = fsd_list_count;
842
843 /* Copyout */
844 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count,
845 sizeof (ioc_list_count), mode)) {
846 ret = EFAULT;
847 goto out;
848 }
849 for (fsdi = list_head(&fsd_list), i = 0;
850 fsdi != NULL && i < ioc_list_count;
851 fsdi = list_next(&fsd_list, fsdi), i++) {
852 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp);
853 int len = strlen(refstr_value(mntstr));
854
855 rw_enter(&fsdi->fsdi_lock, RW_READER);
856 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name,
857 len + 1, mode) ||
858 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param,
859 sizeof (fsdi->fsdi_param), mode)) {
860 ret = EFAULT;
861 }
862 rw_exit(&fsdi->fsdi_lock);
863 refstr_rele(mntstr);
864
865 if (ret != 0)
866 break;
867 }
868
869
870 out:
871 mutex_exit(&fsd_lock);
872 return (ret);
873 }
874
875 static int
876 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
877 {
878 file_t *file;
879 int64_t fd;
880
881 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
882 return (EFAULT);
883
884 if ((file = getf((int)fd)) == NULL) {
885 *rvalp = EBADFD;
886 return (0);
887 }
888
889 mutex_enter(&fsd_lock);
890 *rvalp = fsd_disturber_remove(file->f_vnode->v_vfsp);
891 releasef((int)fd);
892 mutex_exit(&fsd_lock);
893
894 return (0);
895 }
896
897 static int
898 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
899 {
900 fsd_t fsd;
901 int rv;
902
903 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
904 return (EFAULT);
905
906 if ((rv = fsd_check_param(&fsd)) != 0) {
907 *rvalp = rv;
908 return (0);
909 }
910
911 mutex_enter(&fsd_lock);
912 if (fsd_omni_param == NULL)
913 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
914 KM_SLEEP);
915 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
916 mutex_exit(&fsd_lock);
917
918 *rvalp = 0;
919 return (0);
920 }
921
922
923 static int
924 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
925 int *rvalp)
926 {
927 _NOTE(ARGUNUSED(dev));
928 _NOTE(ARGUNUSED(credp));
929
930 int enabled;
931
932 mutex_enter(&fsd_lock);
933 enabled = fsd_enabled;
934 mutex_exit(&fsd_lock);
935
936 if (!enabled && cmd != FSD_ENABLE) {
937 *rvalp = ENOTACTIVE;
938 return (0);
939 }
940
941 switch (cmd) {
942 case FSD_ENABLE:
943 fsd_enable();
944 *rvalp = 0;
945 return (0);
946
947 case FSD_DISABLE:
948 fsd_disable();
949 *rvalp = 0;
950 return (0);
951
952 case FSD_GET_PARAM:
953 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
954
955 case FSD_DISTURB:
956 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
957
958 case FSD_DISTURB_OFF:
959 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp));
960
961 case FSD_DISTURB_OMNI:
962 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp));
963
964 case FSD_DISTURB_OMNI_OFF:
965 mutex_enter(&fsd_lock);
966 if (fsd_omni_param != NULL)
967 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
968 fsd_omni_param = NULL;
969 mutex_exit(&fsd_lock);
970
971 *rvalp = 0;
972 return (0);
973
974 case FSD_GET_LIST:
975 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp));
976
977 case FSD_GET_INFO:
978 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp));
979
980 default:
981 return (ENOTTY);
982 }
983 }
984
985 static struct cb_ops cb_ops = {
986 fsd_open, /* open(9E) */
987 fsd_close, /* close(9E) */
988 nodev, /* strategy(9E) */
989 nodev, /* print(9E) */
990 nodev, /* dump(9E) */
991 nodev, /* read(9E) */
992 nodev, /* write(9E) */
993 fsd_ioctl, /* ioctl(9E) */
994 nodev, /* devmap(9E) */
995 nodev, /* mmap(9E) */
996 nodev, /* segmap(9E) */
997 nochpoll, /* chpoll(9E) */
998 ddi_prop_op, /* prop_op(9E) */
999 NULL, /* streamtab(9E) */
1000 D_MP | D_64BIT, /* cb_flag(9E) */
1001 CB_REV, /* cb_rev(9E) */
1002 nodev, /* aread(9E) */
1003 nodev, /* awrite(9E) */
1004 };
1005
1006 static struct dev_ops dev_ops = {
1007 DEVO_REV, /* driver build version */
1008 0, /* reference count */
1009 fsd_getinfo, /* getinfo */
1010 nulldev,
1011 nulldev, /* probe */
1012 fsd_attach, /* attach */
1013 fsd_detach, /* detach */
1014 nodev,
1015 &cb_ops, /* cb_ops */
1016 NULL, /* bus_ops */
1017 NULL, /* power */
1018 ddi_quiesce_not_needed, /* quiesce */
1019 };
1020
1021 static struct modldrv modldrv = {
1022 &mod_driverops, "Filesystem disturber", &dev_ops
1023 };
1024
1025 static struct modlinkage modlinkage = {
1026 MODREV_1, &modldrv, NULL
1027 };
1028
1029 int
1030 _init(void)
1031 {
1032 return (mod_install(&modlinkage));
1033 }
1034
1035 int
1036 _info(struct modinfo *modinfop)
1037 {
1038 return (mod_info(&modlinkage, modinfop));
1039 }
1040
1041 int
1042 _fini(void)
1043 {
1044 return (mod_remove(&modlinkage));
1045 }
--- EOF ---