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_FSREC(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_FSREC(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_FSREC(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 rw_enter(&listp->fshl_lock, RW_WRITER); \ 137 nodep->fshn_next = \ 138 fsrecp \ 139 ->fshfsr_opv[FSH_##type##_##upper].fshl_head; \ 140 fsrecp->fshfsr_opv[FSH_##type##_##upper].fshl_head \ 141 = nodep; \ 142 rw_exit(&listp->fshl_lock); \ 143 } \ 144 } while (0) 145 146 #define FSH_INSTALL_VN(hooks, fsrecp, listp, nodep, lower, upper) \ 147 FSH_INSTALL(VOP, hooks, fsrecp, listp, nodep, lower, upper) 148 149 #define FSH_INSTALL_VFS(hooks, fsrecp, listp, nodep, lower, upper) \ 150 FSH_INSTALL(VFS, hooks, fsrecp, listp, nodep, lower, upper) 151 152 int 153 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks) 154 { 155 fsh_fsrecord_t *fsrec; 156 fsh_list_t *list; 157 fsh_node_t *node; 158 159 fsrec = FSH_GET_FSREC(vfsp); 160 161 FSH_INSTALL_VN(hooks, fsrec, list, node, open, OPEN); 162 FSH_INSTALL_VN(hooks, fsrec, list, node, close, CLOSE); 163 FSH_INSTALL_VN(hooks, fsrec, list, node, read, READ); 164 FSH_INSTALL_VN(hooks, fsrec, list, node, write, WRITE); 165 FSH_INSTALL_VFS(hooks, fsrec, list, node, mount, MOUNT); 166 FSH_INSTALL_VFS(hooks, fsrec, list, node, unmount, UNMOUNT); 167 FSH_INSTALL_VFS(hooks, fsrec, list, node, root, ROOT); 168 FSH_INSTALL_VFS(hooks, fsrec, list, node, vget, VGET); 169 FSH_INSTALL_VFS(hooks, fsrec, list, node, statfs, STATFS); 170 171 return (0); 172 } 173 174 175 #define FSH_REMOVE(type, hooks, fsrec, list, node, prev, lower, upper) \ 176 do { \ 177 if (hooks->hook_ ## lower == NULL) \ 178 break; \ 179 \ 180 list = &fsrec->fshfsr_opv[FSH_ ## type ## _ ## upper]; \ 181 rw_enter(&list->fshl_lock, RW_WRITER); \ 182 node = list->fshl_head; \ 183 \ 184 if (node == NULL) { \ 185 rw_exit(&list->fshl_lock); \ 186 break; \ 187 } \ 188 \ 189 while (node && \ 190 !(node->fshn_hooki.fshi_fn.hook_ ## lower == \ 191 hooks->hook_ ## lower && \ 192 node->fshn_hooki.fshi_arg == hooks->arg)) { \ 193 prev = node; \ 194 node = node->fshn_next; \ 195 } \ 196 \ 197 if (node == NULL) { \ 198 rw_exit(&list->fshl_lock); \ 199 break; \ 200 } \ 201 \ 202 if (node == list->fshl_head) \ 203 list->fshl_head = node->fshn_next; \ 204 else \ 205 prev->fshn_next = node->fshn_next; \ 206 rw_exit(&list->fshl_lock); \ 207 \ 208 kmem_free(node, sizeof (*node)); \ 209 } while (0) 210 211 #define FSH_REMOVE_VN(hooks, fsrec, list, node, prev, lower, upper) \ 212 FSH_REMOVE(VOP, hooks, fsrec, list, node, prev, lower, upper) 213 214 #define FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, lower, upper) \ 215 FSH_REMOVE(VFS, hooks, fsrec, list, node, prev, lower, upper) 216 217 int 218 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks) 219 { 220 fsh_fsrecord_t *fsrec; 221 fsh_list_t *list; 222 fsh_node_t *node; 223 fsh_node_t *prev; 224 225 fsrec = FSH_GET_FSREC(vfsp); 226 227 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, open, OPEN); 228 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, close, CLOSE); 229 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, read, READ); 230 FSH_REMOVE_VN(hooks, fsrec, list, node, prev, write, WRITE); 231 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, mount, MOUNT); 232 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, unmount, UNMOUNT); 233 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, root, ROOT); 234 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, vget, VGET); 235 FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, statfs, STATFS); 236 237 return (0); 238 } 239 240 241 int 242 fsh_callback_install(fsh_callback_t *fsh_callback) 243 { 244 fsh_callback_node_t *node; 245 246 node = (fsh_callback_node_t *) kmem_alloc(sizeof (*node), KM_SLEEP); 247 node->fshcn_callback = *fsh_callback; 248 249 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER); 250 node->fshcn_next = fsh_global_callback_list.fshcl_head; 251 fsh_global_callback_list.fshcl_head = node; 252 rw_exit(&fsh_global_callback_list.fshcl_lock); 253 254 return (0); 255 } 256 257 int 258 fsh_callback_remove(fsh_callback_t *fsh_callback) 259 { 260 fsh_callback_node_t *node; 261 fsh_callback_node_t *prev; 262 fsh_callback_list_t *list; 263 264 list = &fsh_global_callback_list; 265 266 rw_enter(&list->fshcl_lock, RW_WRITER); 267 node = list->fshcl_head; 268 269 if (node == NULL) { 270 rw_exit(&list->fshcl_lock); 271 return (0); 272 } 273 274 while (node && memcmp(fsh_callback, &node->fshcn_callback, 275 sizeof (*fsh_callback))) { 276 prev = node; 277 node = node->fshcn_next; 278 } 279 280 if (node == NULL) { 281 rw_exit(&list->fshcl_lock); 282 return (0); 283 } 284 285 prev->fshcn_next = node->fshcn_next; 286 kmem_free(node, sizeof (*node)); 287 288 rw_exit(&list->fshcl_lock); 289 return (0); 290 } 291 292 293 294 295 #define FSH_ENABLED(vfsp, enabled) \ 296 do { \ 297 rw_enter(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock, RW_READER); \ 298 *enabled = FSH_GET_FSREC(vfsp)->fshfsr_enabled; \ 299 rw_exit(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock); \ 300 } while (0) 301 302 int 303 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) 304 { 305 fsh_list_t *list; 306 int enabled; 307 int ret; 308 309 FSH_ENABLED((*vpp)->v_vfsp, &enabled); 310 if (!enabled) 311 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct)); 312 313 list = &FSH_GET_FSREC((*vpp)->v_vfsp)->fshfsr_opv[FSH_VOP_OPEN]; 314 rw_enter(&list->fshl_lock, RW_READER); 315 if (list->fshl_head == NULL) 316 ret = (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct); 317 else 318 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct); 319 rw_exit(&list->fshl_lock); 320 321 return (ret); 322 } 323 324 int 325 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, 326 caller_context_t *ct) 327 { 328 fsh_list_t *list; 329 int enabled; 330 int ret; 331 332 FSH_ENABLED(vp->v_vfsp, &enabled); 333 if (!enabled) 334 return ((*(vp->v_op->vop_close))(vp, flag, count, offset, 335 cr, ct)); 336 337 list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_CLOSE]; 338 rw_enter(&list->fshl_lock, RW_READER); 339 if (list->fshl_head == NULL) 340 ret = (*(vp->v_op->vop_close))(vp, flag, count, offset, 341 cr, ct); 342 else 343 ret = fsh_next_close(list->fshl_head, vp, flag, count, 344 offset, cr, ct); 345 rw_exit(&list->fshl_lock); 346 347 return (ret); 348 } 349 350 int 351 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, 352 caller_context_t *ct) 353 { 354 fsh_list_t *list; 355 int enabled; 356 int ret; 357 358 FSH_ENABLED(vp->v_vfsp, &enabled); 359 if (!enabled) 360 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct)); 361 362 list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_READ]; 363 rw_enter(&list->fshl_lock, RW_READER); 364 if (list->fshl_head == NULL) 365 ret = (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct); 366 else 367 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag, 368 cr, ct); 369 rw_exit(&list->fshl_lock); 370 371 return (ret); 372 } 373 374 int 375 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, 376 caller_context_t *ct) 377 { 378 fsh_list_t *list; 379 int enabled; 380 int ret; 381 382 FSH_ENABLED(vp->v_vfsp, &enabled); 383 if (!enabled) 384 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct)); 385 386 list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_WRITE]; 387 rw_enter(&list->fshl_lock, RW_READER); 388 if (list->fshl_head == NULL) 389 ret = (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct); 390 else 391 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag, 392 cr, ct); 393 rw_exit(&list->fshl_lock); 394 395 return (ret); 396 } 397 398 int 399 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 400 { 401 fsh_list_t *list; 402 int ret; 403 404 list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_MOUNT]; 405 rw_enter(&list->fshl_lock, RW_READER); 406 if (list->fshl_head == NULL) 407 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr); 408 else 409 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap, 410 cr); 411 rw_exit(&list->fshl_lock); 412 413 return (ret); 414 } 415 416 int 417 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr) 418 { 419 fsh_list_t *list; 420 int ret; 421 422 list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_UNMOUNT]; 423 rw_enter(&list->fshl_lock, RW_READER); 424 if (list->fshl_head == NULL) 425 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr); 426 else 427 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr); 428 rw_exit(&list->fshl_lock); 429 430 return (ret); 431 } 432 433 int 434 fsh_root(vfs_t *vfsp, vnode_t **vpp) 435 { 436 fsh_list_t *list; 437 int ret; 438 439 list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_ROOT]; 440 rw_enter(&list->fshl_lock, RW_READER); 441 if (list->fshl_head == NULL) 442 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp); 443 else 444 ret = fsh_next_root(list->fshl_head, vfsp, vpp); 445 rw_exit(&list->fshl_lock); 446 447 return (ret); 448 } 449 450 int 451 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp) 452 { 453 fsh_list_t *list; 454 int ret; 455 456 list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_STATFS]; 457 rw_enter(&list->fshl_lock, RW_READER); 458 if (list->fshl_head == NULL) 459 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp); 460 else 461 ret = fsh_next_statfs(list->fshl_head, vfsp, sp); 462 rw_exit(&list->fshl_lock); 463 464 return (ret); 465 } 466 467 int 468 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) 469 { 470 fsh_list_t *list; 471 int ret; 472 473 list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_VGET]; 474 rw_enter(&list->fshl_lock, RW_READER); 475 if (list->fshl_head == NULL) 476 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp); 477 else 478 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp); 479 rw_exit(&list->fshl_lock); 480 481 return (ret); 482 } 483 484 void 485 fsh_exec_create_callbacks(vfs_t *vfsp) 486 { 487 fsh_callback_node_t *node; 488 fsh_callback_t *callback; 489 490 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER); 491 node = fsh_global_callback_list.fshcl_head; 492 while (node) { 493 callback = &node->fshcn_callback; 494 (*(callback->fshc_create))(vfsp, callback->fshc_arg); 495 node = node->fshcn_next; 496 } 497 rw_exit(&fsh_global_callback_list.fshcl_lock); 498 } 499 500 void 501 fsh_exec_destroy_callbacks(vfs_t *vfsp) 502 { 503 fsh_callback_node_t *node; 504 fsh_callback_t *callback; 505 506 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER); 507 node = fsh_global_callback_list.fshcl_head; 508 while (node) { 509 callback = &node->fshcn_callback; 510 (*(callback->fshc_destroy))(vfsp, callback->fshc_arg); 511 node = node->fshcn_next; 512 } 513 rw_exit(&fsh_global_callback_list.fshcl_lock); 514 } 515 516 /* To be used ONLY in vfs_alloc() */ 517 struct fsh_fsrecord * 518 fsh_fsrec_create() 519 { 520 struct fsh_fsrecord *fsrecp; 521 int i; 522 523 fsrecp = (fsh_fsrecord_t *) kmem_alloc(sizeof (*fsrecp), KM_SLEEP); 524 bzero(fsrecp, sizeof (*fsrecp)); 525 526 rw_init(&fsrecp->fshfsr_en_lock, NULL, RW_DRIVER, NULL); 527 fsrecp->fshfsr_enabled = 1; 528 529 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) 530 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER, 531 NULL); 532 return fsrecp; 533 } 534 535 /* To be used ONLY in vfs_free() */ 536 void 537 fsh_fsrec_destroy(fsh_fsrecord_t *fsrecp) 538 { 539 int i; 540 fsh_node_t *node, *next_node; 541 542 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) { 543 node = fsrecp->fshfsr_opv[i].fshl_head; 544 while (node) { 545 next_node = node->fshn_next; 546 kmem_free(node, sizeof (*node)); 547 node = next_node; 548 } 549 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock); 550 } 551 rw_destroy(&fsrecp->fshfsr_en_lock); 552 kmem_free(fsrecp, sizeof (*fsrecp)); 553 } 554 555 556 /* control passing */ 557 int 558 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr, 559 caller_context_t *ct) 560 { 561 if (fsh_node == NULL) 562 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct)); 563 564 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))( 565 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 566 vpp, mode, cr, ct)); 567 568 } 569 570 int 571 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count, 572 offset_t offset, cred_t *cr, caller_context_t *ct) 573 { 574 if (fsh_node == NULL) 575 return ((*(vp->v_op->vop_close))(vp, flag, count, offset, 576 cr, ct)); 577 578 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))( 579 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 580 vp, flag, count, offset, cr, ct)); 581 } 582 583 int 584 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag, 585 cred_t *cr, caller_context_t *ct) 586 { 587 if (fsh_node == NULL) 588 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct)); 589 590 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))( 591 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 592 vp, uiop, ioflag, cr, ct)); 593 } 594 595 int 596 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag, 597 cred_t *cr, caller_context_t *ct) 598 { 599 if (fsh_node == NULL) 600 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct)); 601 602 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))( 603 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 604 vp, uiop, ioflag, cr, ct)); 605 } 606 607 int 608 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp, 609 struct mounta *uap, cred_t *cr) 610 { 611 if (fsh_node == NULL) 612 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr)); 613 614 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))( 615 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 616 vfsp, mvp, uap, cr)); 617 } 618 619 int 620 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr) 621 { 622 if (fsh_node == NULL) 623 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr)); 624 625 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))( 626 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 627 vfsp, flag, cr)); 628 } 629 630 int 631 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp) 632 { 633 if (fsh_node == NULL) 634 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp)); 635 636 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))( 637 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 638 vfsp, vpp)); 639 } 640 641 int 642 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp) 643 { 644 if (fsh_node == NULL) 645 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp)); 646 647 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))( 648 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 649 vfsp, sp)); 650 } 651 652 int 653 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) 654 { 655 if (fsh_node == NULL) 656 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp)); 657 658 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))( 659 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 660 vfsp, vpp, fidp)); 661 }