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 #include <sys/sunddi.h>
17 #include <sys/fsh.h>
18 #include <sys/fsh_impl.h>
19 #include <sys/ksynch.h>
20 #include <sys/types.h>
21 #include <sys/vfs.h>
22 #include <sys/vnode.h>
23
24 /*
25 * TODO:
26 * - support more operations
27 * - describe the design of FSH in a comment
28 * - add DTrace and kstat
29 */
30
31 #define FSH_VFS_MOUNT 0
32 #define FSH_VFS_UNMOUNT 1
33 #define FSH_VFS_ROOT 2
34 #define FSH_VFS_STATFS 3
35 #define FSH_VFS_VGET 4
36
37 #define FSH_VOP_OPEN 5
38 #define FSH_VOP_CLOSE 6
39 #define FSH_VOP_READ 7
40 #define FSH_VOP_WRITE 8
41
42 #define FSH_SUPPORTED_OPS_COUNT 9
43
44 typedef union fsh_fn {
45 FSH_OPS;
46 } fsh_fn_t;
47
48 typedef struct fsh_int {
49 fsh_fn_t fshi_fn;
50 void *fshi_arg;
51 } fsh_int_t;
52
53
54 struct fsh_node {
55 fsh_int_t fshn_hooki;
56 struct fsh_node *fshn_next;
57 };
58 /* typedef struct fsh_node fsh_node_t; in fsh.h */
59
60 /*
61 * fshl_lock is being read-locked by every call to a fsh_vop/vfsop() for
62 * entire execution. This way, we guarantee that a list of hooks is unchanged
63 * during one fop_foo()/fsop_foo() execution.
64 */
65 typedef struct fsh_list {
66 krwlock_t fshl_lock;
67 fsh_node_t *fshl_head;
68 } fsh_list_t;
69
70 typedef fsh_list_t fsh_opvector[FSH_SUPPORTED_OPS_COUNT];
71 typedef fsh_opvector fsh_opvector_t;
72
73
74 typedef struct fsh_fsrecord {
75 krwlock_t fshfsr_en_lock; /* lock for fshfsr_enabled */
76 int fshfsr_enabled;
77 fsh_opvector_t fshfsr_opv;
78 } fsh_fsrecord_t;
79
80
81 typedef struct fsh_callback_node {
82 fsh_callback_t fshcn_callback;
83 struct fsh_callback_node *fshcn_next;
84 } fsh_callback_node_t;
85
86 typedef struct fsh_callback_list {
87 krwlock_t fshcl_lock;
88 fsh_callback_node_t *fshcl_head;
89 } fsh_callback_list_t;
90
91 fsh_callback_list_t fsh_global_callback_list;
92
93 /*
94 * It is assumed that VFS_HOLD() has been called before calling any of the
95 * fsh_fs_xxx()/fsh_hook_xxx() API. VFS_RELE() should be called after.
96 */
97
98 #define FSH_GET_FSRECP(vfsp) (vfsp->vfs_fshrecord)
99
100 int
101 fsh_fs_enable(vfs_t *vfsp)
102 {
103 fsh_fsrecord_t *fsrec;
104
105 fsrec = FSH_GET_FSRECP(vfsp);
106 rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
107 fsrec->fshfsr_enabled = 1;
108 rw_exit(&fsrec->fshfsr_en_lock);
109
110 return (0);
111 }
112
113 int
114 fsh_fs_disable(vfs_t *vfsp)
115 {
116 fsh_fsrecord_t *fsrec;
117
118 fsrec = FSH_GET_FSRECP(vfsp);
119 rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
120 fsrec->fshfsr_enabled = 0;
121 rw_exit(&fsrec->fshfsr_en_lock);
122
123 return (0);
124 }
125
126
127 #define FSH_INSTALL(type, hooks, fsrecp, listp, nodep, lower, upper) \
128 do { \
129 if (hooks->hook_##lower) { \
130 nodep = (fsh_node_t *) kmem_alloc(sizeof (*nodep), \
131 KM_SLEEP); \
132 nodep->fshn_hooki.fshi_fn.hook_##lower = \
133 hooks->hook_##lower; \
134 nodep->fshn_hooki.fshi_arg = hooks->arg; \
135 \
136 listp = &fsrecp->fshfsr_opv[FSH_##type##_##upper]; \
137 rw_enter(&listp->fshl_lock, RW_WRITER); \
138 nodep->fshn_next = \
139 fsrecp \
140 ->fshfsr_opv[FSH_##type##_##upper].fshl_head; \
141 fsrecp->fshfsr_opv[FSH_##type##_##upper].fshl_head \
142 = nodep; \
143 rw_exit(&listp->fshl_lock); \
144 } \
145 _NOTE(CONSTCOND) \
146 } while (0)
147
148 #define FSH_INSTALL_VN(hooks, fsrecp, listp, nodep, lower, upper) \
149 FSH_INSTALL(VOP, hooks, fsrecp, listp, nodep, lower, upper)
150
151 #define FSH_INSTALL_VFS(hooks, fsrecp, listp, nodep, lower, upper) \
152 FSH_INSTALL(VFS, hooks, fsrecp, listp, nodep, lower, upper)
153
154 int
155 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
156 {
157 fsh_fsrecord_t *fsrec;
158 fsh_list_t *list;
159 fsh_node_t *node;
160
161 fsrec = FSH_GET_FSRECP(vfsp);
162
163 FSH_INSTALL_VN(hooks, fsrec, list, node, open, OPEN);
164 FSH_INSTALL_VN(hooks, fsrec, list, node, close, CLOSE);
165 FSH_INSTALL_VN(hooks, fsrec, list, node, read, READ);
166 FSH_INSTALL_VN(hooks, fsrec, list, node, write, WRITE);
167 FSH_INSTALL_VFS(hooks, fsrec, list, node, mount, MOUNT);
168 FSH_INSTALL_VFS(hooks, fsrec, list, node, unmount, UNMOUNT);
169 FSH_INSTALL_VFS(hooks, fsrec, list, node, root, ROOT);
170 FSH_INSTALL_VFS(hooks, fsrec, list, node, vget, VGET);
171 FSH_INSTALL_VFS(hooks, fsrec, list, node, statfs, STATFS);
172
173 return (0);
174 }
175
176
177 #define FSH_REMOVE(type, hooks, fsrec, list, node, prev, lower, upper) \
178 do { \
179 if (hooks->hook_ ## lower == NULL) \
180 break; \
181 \
182 list = &fsrec->fshfsr_opv[FSH_ ## type ## _ ## upper]; \
183 rw_enter(&list->fshl_lock, RW_WRITER); \
184 node = list->fshl_head; \
185 \
186 if (node == NULL) { \
187 rw_exit(&list->fshl_lock); \
188 break; \
189 } \
190 \
191 while (node && \
192 !(node->fshn_hooki.fshi_fn.hook_ ## lower == \
193 hooks->hook_ ## lower && \
194 node->fshn_hooki.fshi_arg == hooks->arg)) { \
195 prev = node; \
196 node = node->fshn_next; \
197 } \
198 \
199 if (node == NULL) { \
200 rw_exit(&list->fshl_lock); \
201 break; \
202 } \
203 \
204 if (node == list->fshl_head) \
205 list->fshl_head = node->fshn_next; \
206 else \
207 prev->fshn_next = node->fshn_next; \
208 rw_exit(&list->fshl_lock); \
209 \
210 kmem_free(node, sizeof (*node)); \
211 _NOTE(CONSTCOND) \
212 } while (0)
213
214 #define FSH_REMOVE_VN(hooks, fsrec, list, node, prev, lower, upper) \
215 FSH_REMOVE(VOP, hooks, fsrec, list, node, prev, lower, upper)
216
217 #define FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, lower, upper) \
218 FSH_REMOVE(VFS, hooks, fsrec, list, node, prev, lower, upper)
219
220 int
221 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks)
222 {
223 fsh_fsrecord_t *fsrec;
224 fsh_list_t *list;
225 fsh_node_t *node;
226 fsh_node_t *prev;
227
228 fsrec = FSH_GET_FSRECP(vfsp);
229
230 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, open, OPEN);
231 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, close, CLOSE);
232 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, read, READ);
233 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, write, WRITE);
234 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, mount, MOUNT);
235 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, unmount, UNMOUNT);
236 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, root, ROOT);
237 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, vget, VGET);
238 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, statfs, STATFS);
239
240 return (0);
241 }
242
243
244 int
245 fsh_callback_install(fsh_callback_t *fsh_callback)
246 {
247 fsh_callback_node_t *node;
248
249 node = (fsh_callback_node_t *) kmem_alloc(sizeof (*node), KM_SLEEP);
250 node->fshcn_callback = *fsh_callback;
251
252 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER);
253 node->fshcn_next = fsh_global_callback_list.fshcl_head;
254 fsh_global_callback_list.fshcl_head = node;
255 rw_exit(&fsh_global_callback_list.fshcl_lock);
256
257 return (0);
258 }
259
260 int
261 fsh_callback_remove(fsh_callback_t *fsh_callback)
262 {
263 fsh_callback_node_t *node;
264 fsh_callback_node_t *prev;
265 fsh_callback_list_t *list;
266
267 list = &fsh_global_callback_list;
268
269 rw_enter(&list->fshcl_lock, RW_WRITER);
270 node = list->fshcl_head;
271
272 if (node == NULL) {
273 rw_exit(&list->fshcl_lock);
274 return (0);
275 }
276
277 while (node && memcmp(fsh_callback, &node->fshcn_callback,
278 sizeof (*fsh_callback))) {
279 prev = node;
280 node = node->fshcn_next;
281 }
282
283 if (node == NULL) {
284 rw_exit(&list->fshcl_lock);
285 return (0);
286 }
287
288 prev->fshcn_next = node->fshcn_next;
289 kmem_free(node, sizeof (*node));
290
291 rw_exit(&list->fshcl_lock);
292 return (0);
293 }
294
295
296
297
298 #define FSH_ENABLED(vfsp, enabled) \
299 do { \
300 rw_enter(&FSH_GET_FSRECP(vfsp)->fshfsr_en_lock, RW_READER); \
301 *enabled = FSH_GET_FSRECP(vfsp)->fshfsr_enabled; \
302 rw_exit(&FSH_GET_FSRECP(vfsp)->fshfsr_en_lock); \
303 _NOTE(CONSTCOND) \
304 } while (0)
305
306 int
307 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
308 {
309 fsh_list_t *list;
310 int enabled;
311 int ret;
312
313 FSH_ENABLED((*vpp)->v_vfsp, &enabled);
314 if (!enabled)
315 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct));
316
317 list = &FSH_GET_FSRECP((*vpp)->v_vfsp)->fshfsr_opv[FSH_VOP_OPEN];
318 rw_enter(&list->fshl_lock, RW_READER);
319 if (list->fshl_head == NULL)
320 ret = (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct);
321 else
322 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct);
323 rw_exit(&list->fshl_lock);
324
325 return (ret);
326 }
327
328 int
329 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
330 caller_context_t *ct)
331 {
332 fsh_list_t *list;
333 int enabled;
334 int ret;
335
336 FSH_ENABLED(vp->v_vfsp, &enabled);
337 if (!enabled)
338 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
339 cr, ct));
340
341 list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_CLOSE];
342 rw_enter(&list->fshl_lock, RW_READER);
343 if (list->fshl_head == NULL)
344 ret = (*(vp->v_op->vop_close))(vp, flag, count, offset,
345 cr, ct);
346 else
347 ret = fsh_next_close(list->fshl_head, vp, flag, count,
348 offset, cr, ct);
349 rw_exit(&list->fshl_lock);
350
351 return (ret);
352 }
353
354 int
355 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
356 caller_context_t *ct)
357 {
358 fsh_list_t *list;
359 int enabled;
360 int ret;
361
362 FSH_ENABLED(vp->v_vfsp, &enabled);
363 if (!enabled)
364 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
365
366 list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_READ];
367 rw_enter(&list->fshl_lock, RW_READER);
368 if (list->fshl_head == NULL)
369 ret = (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct);
370 else
371 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag,
372 cr, ct);
373 rw_exit(&list->fshl_lock);
374
375 return (ret);
376 }
377
378 int
379 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
380 caller_context_t *ct)
381 {
382 fsh_list_t *list;
383 int enabled;
384 int ret;
385
386 FSH_ENABLED(vp->v_vfsp, &enabled);
387 if (!enabled)
388 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
389
390 list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_WRITE];
391 rw_enter(&list->fshl_lock, RW_READER);
392 if (list->fshl_head == NULL)
393 ret = (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct);
394 else
395 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag,
396 cr, ct);
397 rw_exit(&list->fshl_lock);
398
399 return (ret);
400 }
401
402 int
403 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
404 {
405 fsh_list_t *list;
406 int ret;
407
408 list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_MOUNT];
409 rw_enter(&list->fshl_lock, RW_READER);
410 if (list->fshl_head == NULL)
411 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr);
412 else
413 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap,
414 cr);
415 rw_exit(&list->fshl_lock);
416
417 return (ret);
418 }
419
420 int
421 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
422 {
423 fsh_list_t *list;
424 int ret;
425
426 list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_UNMOUNT];
427 rw_enter(&list->fshl_lock, RW_READER);
428 if (list->fshl_head == NULL)
429 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr);
430 else
431 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr);
432 rw_exit(&list->fshl_lock);
433
434 return (ret);
435 }
436
437 int
438 fsh_root(vfs_t *vfsp, vnode_t **vpp)
439 {
440 fsh_list_t *list;
441 int ret;
442
443 list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_ROOT];
444 rw_enter(&list->fshl_lock, RW_READER);
445 if (list->fshl_head == NULL)
446 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp);
447 else
448 ret = fsh_next_root(list->fshl_head, vfsp, vpp);
449 rw_exit(&list->fshl_lock);
450
451 return (ret);
452 }
453
454 int
455 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp)
456 {
457 fsh_list_t *list;
458 int ret;
459
460 list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_STATFS];
461 rw_enter(&list->fshl_lock, RW_READER);
462 if (list->fshl_head == NULL)
463 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp);
464 else
465 ret = fsh_next_statfs(list->fshl_head, vfsp, sp);
466 rw_exit(&list->fshl_lock);
467
468 return (ret);
469 }
470
471 int
472 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
473 {
474 fsh_list_t *list;
475 int ret;
476
477 list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_VGET];
478 rw_enter(&list->fshl_lock, RW_READER);
479 if (list->fshl_head == NULL)
480 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp);
481 else
482 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp);
483 rw_exit(&list->fshl_lock);
484
485 return (ret);
486 }
487
488 void
489 fsh_exec_create_callbacks(vfs_t *vfsp)
490 {
491 fsh_callback_node_t *node;
492 fsh_callback_t *callback;
493
494 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
495 node = fsh_global_callback_list.fshcl_head;
496 while (node) {
497 callback = &node->fshcn_callback;
498 (*(callback->fshc_create))(vfsp, callback->fshc_arg);
499 node = node->fshcn_next;
500 }
501 rw_exit(&fsh_global_callback_list.fshcl_lock);
502 }
503
504 void
505 fsh_exec_destroy_callbacks(vfs_t *vfsp)
506 {
507 fsh_callback_node_t *node;
508 fsh_callback_t *callback;
509
510 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
511 node = fsh_global_callback_list.fshcl_head;
512 while (node) {
513 callback = &node->fshcn_callback;
514 (*(callback->fshc_destroy))(vfsp, callback->fshc_arg);
515 node = node->fshcn_next;
516 }
517 rw_exit(&fsh_global_callback_list.fshcl_lock);
518 }
519
520 /* To be used ONLY in vfs_alloc() */
521 struct fsh_fsrecord *
522 fsh_fsrec_create()
523 {
524 struct fsh_fsrecord *fsrecp;
525 int i;
526
527 fsrecp = (fsh_fsrecord_t *) kmem_alloc(sizeof (*fsrecp), KM_SLEEP);
528 bzero(fsrecp, sizeof (*fsrecp));
529
530 rw_init(&fsrecp->fshfsr_en_lock, NULL, RW_DRIVER, NULL);
531 fsrecp->fshfsr_enabled = 0; // DEBUG
532
533 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++)
534 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER,
535 NULL);
536 return (fsrecp);
537 }
538
539 /* To be used ONLY in vfs_free() */
540 void
541 fsh_fsrec_destroy(fsh_fsrecord_t *fsrecp)
542 {
543 int i;
544 fsh_node_t *node, *next_node;
545
546 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) {
547 node = fsrecp->fshfsr_opv[i].fshl_head;
548 while (node) {
549 next_node = node->fshn_next;
550 kmem_free(node, sizeof (*node));
551 node = next_node;
552 }
553 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock);
554 }
555 rw_destroy(&fsrecp->fshfsr_en_lock);
556 kmem_free(fsrecp, sizeof (*fsrecp));
557 }
558
559
560 /* control passing */
561 int
562 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr,
563 caller_context_t *ct)
564 {
565 if (fsh_node == NULL)
566 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct));
567
568 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))(
569 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
570 vpp, mode, cr, ct));
571
572 }
573
574 int
575 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count,
576 offset_t offset, cred_t *cr, caller_context_t *ct)
577 {
578 if (fsh_node == NULL)
579 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
580 cr, ct));
581
582 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))(
583 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
584 vp, flag, count, offset, cr, ct));
585 }
586
587 int
588 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
589 cred_t *cr, caller_context_t *ct)
590 {
591 if (fsh_node == NULL)
592 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
593
594 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))(
595 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
596 vp, uiop, ioflag, cr, ct));
597 }
598
599 int
600 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
601 cred_t *cr, caller_context_t *ct)
602 {
603 if (fsh_node == NULL)
604 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
605
606 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))(
607 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
608 vp, uiop, ioflag, cr, ct));
609 }
610
611 int
612 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp,
613 struct mounta *uap, cred_t *cr)
614 {
615 if (fsh_node == NULL)
616 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
617
618 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))(
619 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
620 vfsp, mvp, uap, cr));
621 }
622
623 int
624 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr)
625 {
626 if (fsh_node == NULL)
627 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
628
629 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))(
630 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
631 vfsp, flag, cr));
632 }
633
634 int
635 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp)
636 {
637 if (fsh_node == NULL)
638 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
639
640 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))(
641 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
642 vfsp, vpp));
643 }
644
645 int
646 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp)
647 {
648 if (fsh_node == NULL)
649 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
650
651 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))(
652 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
653 vfsp, sp));
654 }
655
656 int
657 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
658 {
659 if (fsh_node == NULL)
660 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
661
662 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))(
663 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
664 vfsp, vpp, fidp));
665 }