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 }