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/debug.h> 17 #include <sys/fsh.h> 18 #include <sys/fsh_impl.h> 19 #include <sys/ksynch.h> 20 #include <sys/sunddi.h> 21 #include <sys/types.h> 22 #include <sys/vfs.h> 23 #include <sys/vnode.h> 24 25 /* 26 * TODO: 27 * - support more operations 28 * - adjust the code for use of kernel list operations 29 * - add big theory about callbacks 30 */ 31 32 /* 33 * Filesystem hook framework (FSH) 34 * 35 * 1. Abstract. 36 * The main goal of the filesystem hook framework is to provide an easy way to 37 * inject consumer-defined behaviour into vfs/vnode calls. Because of what 38 * zones and ZFS offer, we narrow our hooking system to whole filesystems, not 39 * single vnodes or filesystem subtrees. 40 * 41 * 2. Overview. 42 * fsh_t is the main object in the FSH. An fsh_t is a structure containing: 43 * - pointers to hookin functions (hook_foo, where foo is the name of 44 * a corresponding vnodeop/vfsop) 45 * - a pointer to an argument to pass (this is shared for all the 46 * hooks in a given fsh_t) 47 * 48 * We install a fsh_t on a whole filesystem, but one fsh_t can be installed on 49 * many filesystems. 50 * 51 * 3. Usage. 52 * It is assumed that vfs_t/vnode_t that are passed to fsh_foo() functions are 53 * held by the caller. 54 * 55 * fsh_t is a structure filled out by the consumer. If a consumer does not 56 * want to add/remove a hook for function foo(), he should fill the 57 * hook_foo() field of fsh_t with NULL before calling 58 * fsh_hook_install/remove(). The type of hook_foo() is the type of foo() with 59 * two additional arguments: 60 * - const fsh_node_t *fsh_node - this argument MUST be passed to 61 * hook_next_foo(). FSH would't know which hook to execute next 62 * without it. 63 * - void *arg - this is the argument passed with fsh_t during 64 * installation 65 * All the information passed with fsh_t is copied by FSH, so it's safe for 66 * the caller to do anything with it. Keep in mind that this structure is also 67 * used for removing hooks, so the consumer should somehow remember all of 68 * it's contents. 69 * 70 * Every hook function is responsible for passing the control to the next 71 * hook associated with a particular call. In order to provide an easy way to 72 * modify the behaviour of a function call both before and after the 73 * underlying vfsop/vnodeop (or next hook) execution, a hook has to call 74 * fsh_next_foo() at some point. This function does necessary internal 75 * operations and calls the next hook, until there's no hook left, then it 76 * calls the underlying vfsop/vnodeop. 77 * Example: 78 * my_freefs(const fsh_node_t *fsh_node, void *arg, vfs_t *vfsp) { 79 * cmn_err(CE_NOTE, "freefs called!\n"); 80 * return (fsh_next_freefs(fsh_node, vfsp)); 81 * } 82 * 83 * A consumer might want to receive some notifications about vfs_t entries 84 * being created/destroyed. There's a fsh_callback_t structure provided to 85 * install such callbacks. 86 * 87 * 4. API (fsh.h) 88 * fsh_fs_enable(vfs_t *vfsp) 89 * fsh_fs_disable(vfs_t *vfsp) 90 * Enables/disables fshook per filesystem. 91 * 92 * fsh_hook_install(vfs_t *vfsp, fsh_t *fsh) 93 * Installs hooks on vfsp filesystem. It's important that hooks are 94 * executed in LIFO installation order, which means that if there are 95 * hooks A and B installed in this order, B is going to be execute 96 * before A. 97 * 98 * fsh_hook_remove(vfs_t *vfsp, fsh_t *fsh) 99 * Removes hooks from vfsp filesystem. 100 * 101 * fsh_next_xxx(fsh_node_t *fsh_node, void *arg, ARGUMENTS) 102 * This is the function which should be called once in every hook. It 103 * does the necessary internal operations and passes control to the 104 * next hook or, if there's no hook left, to the underlying 105 * vfsop/vnodeop. 106 * 107 * fsh_callback_install(fsh_callback_t *fsh_callback) 108 * fsh_callback_remove(fsh_callback_t *fsh_callback) 109 * Installs/remove a callback for vfs_t mount/free. The mount callback 110 * is executed right before domount() returns. The free callback is 111 * called right before VFS_FREEVFS() is called. 112 * 113 * 5. API for vfs.c and vnode.c (fsh_impl.h) 114 * fsh_init() 115 * This call has to be done in vfsinit(). It initialises the FSH. It 116 * is absolutely necessary that this call is made before any other FSH 117 * operation. 118 * 119 * fsh_exec_mount_callbacks(vfs_t *vfsp) 120 * fsh_exec_free_callbacks(vfs_t *vfsp) 121 * Used to execute all FSH callback for vfs_t mount/free. 122 * 123 * fsh_fsrec_destroy(struct fsh_fsrecord *fsrecp) 124 * Destroys a fsh_fsrecord structure. 125 * 126 * fsh_foo(ARGUMENTS) 127 * Function used to start executing the hook chain for a given call 128 * (foo). 129 * 130 * 6. Internals. 131 * fsh_fsrecord_t is a structure which lives inside vfs_t. 132 * fsh_fsrecord_t contains: 133 * - an array of fsh_list_t, one for each vfsop/vnodeop. fsh_list_t 134 * is just a list of hooks for a particular vfsop/vnodeop 135 * - a flag which tells if FSH is enabled on this filesystem 136 * 137 * Unfortunately, because of unexpected behaviour of some filesystems (no 138 * use of vfs_alloc()/vfs_init()) there's no good place to initialise the 139 * fsh_fshrecord structure. The approach being used here is to check if it's 140 * initialised in every call. Because of the fact that no lock could be used 141 * here (the same problem with initialisation), a spinlock is used. This is 142 * explained in more detail in a comment before FSH_PREPARE_FSREC(), a macro 143 * that should be used whenever a vfsp->vfs_fshrecord needs to be fetched. 144 * After doing that, it's completely safe to keep this pointer locally, 145 * because it won't change until vfs_free() is called. 146 * 147 * fsh_list_t is a RW locked hook list, designed to containt hooks for one 148 * operation (that's why it's nodes don't contain such information). Every 149 * hook is internally kept as an fsh_int_t structure, which is filled out 150 * using information from a consumer-provided fsh_t. The fsh_node_t is just 151 * this list node containig fsh_int_t. Since fsh_list_t is an RW locked list, 152 * installing and removing hooks may cause delays in filesystem operations. 153 * 154 * fsh_next_xxx() 155 * This function is quite simple. It takes the next node pointer from 156 * fsh_node_t passed to this function and passes control to the next hook or 157 * to the underlying vnodeop/vfsop. 158 * 159 * Callbacks installed with fsh_callback_install/remove() are executed by 160 * calling fsh_exec_mount/fre_callbacks() in domount()/vfs_rele() (when 161 * vfs_t's reference count drops to 0). 162 * 163 * 7. Concurrency 164 * FSH does no vfs_t nor vnode_t locking. It is expected that whenever it is 165 * needed, the consumer does that before/after calling FSH API. 166 * 167 * It is unsafe to call fsh_foo() for a given vfs_t when it's being destroyed. 168 * It's because of the fact that it's expected that vfs_fshrecord is set only 169 * once for the whole vfs_t lifetime. 170 */ 171 172 173 /* Structure used for mount/free callbacks. */ 174 static fsh_callback_list_t fsh_global_callback_list; 175 176 /* 177 * A reserved pointer to FSH. It is used because of the method chosen for 178 * solving concurrency issues for vfs_fshrecord. The full explanation 179 * is in the big theory statement at the beginning of this file. 180 * It is initialised in fsh_init(). 181 */ 182 static void *fsh_res_ptr; 183 184 static struct fsh_fsrecord *fsh_fsrec_create(); 185 186 /* 187 * Important note: 188 * Before using this macro, fsh_init() MUST be called. We do that in 189 * vfsinit()@vfs.c. 190 * 191 * One would ask, why isn't the vfsp->vfs_fshrecord initialised when the 192 * vfs_t is created. Unfortunately, some filesystems (e.g. fifofs) does not 193 * call vfs_init() or even vfs_alloc(), It's possible that some unbundled 194 * filesystems could do the same thing. That's why this macro is introduced. 195 * It should be called before any code that needs access to vfs_fshrecord. 196 * 197 * Locking: 198 * There are no locks here, because there's no good place to initialise 199 * the lock. Concurrency issues are solved by using atomic instructions 200 * and a spinlock, which is spinning only once for a given vfs_t. Because 201 * of that, the usage of spinlock isn't bad at all. 202 * 203 * How it works: 204 * a) if vfsp->vfs_fshrecord is NULL, atomic_cas_ptr changes it to 205 * fsh_res_ptr. That's a signal for other calls, that the structure 206 * is being initialised. Then the creation of vfs_fshrecord is done. 207 * b) if vfsp->vfs_fshrecord is fsh_res_ptr, that means we have to wait, 208 * because vfs_fshrecord is being initialised by another call. 209 * c) other cases: 210 * vfs_fshrecord is already initialised, so we can use it. 211 */ 212 #define FSH_PREPARE_FSREC(vfsp) \ 213 do { \ 214 fsh_fsrecord_t *fsrec; \ 215 \ 216 while ((fsrec = atomic_cas_ptr(&(vfsp)->vfs_fshrecord, NULL, \ 217 fsh_res_ptr)) == fsh_res_ptr); \ 218 if ((fsrec) == NULL) { \ 219 atomic_swap_ptr(&(vfsp)->vfs_fshrecord, \ 220 fsh_fsrec_create()); \ 221 } \ 222 _NOTE(CONSTCOND) \ 223 } while (0) 224 225 /* 226 * API for enabling/disabling FSH per vfs_t. 227 * Atomic operations are used for changing vfs_fshrecord->fshfsr_enabled. 228 */ 229 void 230 fsh_fs_enable(vfs_t *vfsp) 231 { 232 FSH_PREPARE_FSREC(vfsp); 233 atomic_or_uint(&vfsp->vfs_fshrecord->fshfsr_enabled, 1); 234 } 235 236 void 237 fsh_fs_disable(vfs_t *vfsp) 238 { 239 FSH_PREPARE_FSREC(vfsp); 240 atomic_and_uint(&vfsp->vfs_fshrecord->fshfsr_enabled, 0); 241 } 242 243 /* 244 * This macro, like FSH_REMOVE is introduced because of the fact that 245 * the code for installing a hook looks almost exactly the same for 246 * every vop/vfsop. 247 * The usage of these macros is pretty simple. One has to provide pointers to 248 * fsh_t, fsh_fsrecord_t and a name of operation to be installed both in 249 * lowercase and uppercase. 250 */ 251 #define FSH_INSTALL(type, hooks, fsrec, lower, upper) \ 252 do { \ 253 fsh_node_t *node; \ 254 fsh_list_t *list; \ 255 \ 256 if (hooks->hook_##lower) { \ 257 node = (fsh_node_t *)kmem_alloc(sizeof (*node), \ 258 KM_SLEEP); \ 259 node->fshn_hooki.fshi_fn.hook_##lower = \ 260 hooks->hook_##lower; \ 261 node->fshn_hooki.fshi_arg = hooks->arg; \ 262 \ 263 list = &fsrec->fshfsr_opv[FSH_##type##_##upper]; \ 264 rw_enter(&list->fshl_lock, RW_WRITER); \ 265 node->fshn_next = \ 266 fsrec \ 267 ->fshfsr_opv[FSH_##type##_##upper].fshl_head; \ 268 fsrec->fshfsr_opv[FSH_##type##_##upper].fshl_head \ 269 = node; \ 270 rw_exit(&list->fshl_lock); \ 271 } \ 272 _NOTE(CONSTCOND) \ 273 } while (0) 274 275 #define FSH_INSTALL_VN(hooks, fsrec, lower, upper) \ 276 FSH_INSTALL(VOP, hooks, fsrec, lower, upper) 277 278 #define FSH_INSTALL_VFS(hooks, fsrec, lower, upper) \ 279 FSH_INSTALL(VFS, hooks, fsrec, lower, upper) 280 281 void 282 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks) 283 { 284 fsh_fsrecord_t *fsrec; 285 286 FSH_PREPARE_FSREC(vfsp); 287 fsrec = vfsp->vfs_fshrecord; 288 289 FSH_INSTALL_VN(hooks, fsrec, open, OPEN); 290 FSH_INSTALL_VN(hooks, fsrec, close, CLOSE); 291 FSH_INSTALL_VN(hooks, fsrec, read, READ); 292 FSH_INSTALL_VN(hooks, fsrec, write, WRITE); 293 FSH_INSTALL_VFS(hooks, fsrec, mount, MOUNT); 294 FSH_INSTALL_VFS(hooks, fsrec, unmount, UNMOUNT); 295 FSH_INSTALL_VFS(hooks, fsrec, root, ROOT); 296 FSH_INSTALL_VFS(hooks, fsrec, vget, VGET); 297 FSH_INSTALL_VFS(hooks, fsrec, statfs, STATFS); 298 } 299 300 /* 301 * See comment above FSH_INSTALL. 302 * Note: This macro does nothing when a hook to remove was not found. 303 */ 304 #define FSH_REMOVE(type, hooks, fsrec, lower, upper) \ 305 do { \ 306 fsh_node_t *node, *prev; \ 307 fsh_list_t *list; \ 308 if (hooks->hook_##lower == NULL) \ 309 break; \ 310 \ 311 list = &fsrec->fshfsr_opv[FSH_##type##_##upper]; \ 312 rw_enter(&list->fshl_lock, RW_WRITER); \ 313 node = list->fshl_head; \ 314 \ 315 if (node == NULL) { \ 316 rw_exit(&list->fshl_lock); \ 317 break; \ 318 } \ 319 \ 320 while (node && \ 321 !(node->fshn_hooki.fshi_fn.hook_##lower == \ 322 hooks->hook_##lower && \ 323 node->fshn_hooki.fshi_arg == hooks->arg)) { \ 324 prev = node; \ 325 node = node->fshn_next; \ 326 } \ 327 \ 328 if (node == NULL) { \ 329 rw_exit(&list->fshl_lock); \ 330 break; \ 331 } \ 332 \ 333 if (node == list->fshl_head) \ 334 list->fshl_head = node->fshn_next; \ 335 else \ 336 prev->fshn_next = node->fshn_next; \ 337 rw_exit(&list->fshl_lock); \ 338 \ 339 kmem_free(node, sizeof (*node)); \ 340 _NOTE(CONSTCOND) \ 341 } while (0) 342 343 #define FSH_REMOVE_VN(hooks, fsrec, lower, upper) \ 344 FSH_REMOVE(VOP, hooks, fsrec, lower, upper) 345 346 #define FSH_REMOVE_VFS(hooks, fsrec, lower, upper) \ 347 FSH_REMOVE(VFS, hooks, fsrec, lower, upper) 348 349 void 350 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks) 351 { 352 fsh_fsrecord_t *fsrec; 353 354 FSH_PREPARE_FSREC(vfsp); 355 fsrec = vfsp->vfs_fshrecord; 356 357 FSH_REMOVE_VN(hooks, fsrec, open, OPEN); 358 FSH_REMOVE_VN(hooks, fsrec, close, CLOSE); 359 FSH_REMOVE_VN(hooks, fsrec, read, READ); 360 FSH_REMOVE_VN(hooks, fsrec, write, WRITE); 361 FSH_REMOVE_VFS(hooks, fsrec, mount, MOUNT); 362 FSH_REMOVE_VFS(hooks, fsrec, unmount, UNMOUNT); 363 FSH_REMOVE_VFS(hooks, fsrec, root, ROOT); 364 FSH_REMOVE_VFS(hooks, fsrec, vget, VGET); 365 FSH_REMOVE_VFS(hooks, fsrec, statfs, STATFS); 366 } 367 368 /* TODO: check which hooks are installed */ 369 void 370 fsh_hook_check(vfs_t *vfsp, fsh_t *hooks, fsh_t *mask) 371 { 372 _NOTE(ARGUNUSED(vfsp)); 373 _NOTE(ARGUNUSED(hooks)); 374 _NOTE(ARGUNUSED(mask)); 375 } 376 377 /* 378 * API for installing/removing global mount/free callbacks. 379 * It's safe to call these functions whenever after fsh_init() was called. 380 * The fsh_global_callback_list is rwlocked. fsh_callback_install/remove() are 381 * the only writers and fsh_exec_mount/free_callbacks() are the only readers. 382 */ 383 void 384 fsh_callback_install(fsh_callback_t *fsh_callback) 385 { 386 fsh_callback_node_t *node; 387 388 node = (fsh_callback_node_t *)kmem_alloc(sizeof (*node), KM_SLEEP); 389 node->fshcn_callback = *fsh_callback; 390 391 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER); 392 node->fshcn_next = fsh_global_callback_list.fshcl_head; 393 fsh_global_callback_list.fshcl_head = node; 394 rw_exit(&fsh_global_callback_list.fshcl_lock); 395 } 396 397 /* 398 * fsh_callback_t objects are compared by a simple memcmp() here. That's 399 * by design, because we'd like to be absolutely sure that we delete the 400 * callbacks we wanted (the same argument and callback functions). 401 * 402 * TODO: Adding an unique ID to the fsh_callback_t is worth considering, 403 * but it's not yet implemented. 404 */ 405 void 406 fsh_callback_remove(fsh_callback_t *fsh_callback) 407 { 408 fsh_callback_node_t *node; 409 fsh_callback_node_t *prev; 410 fsh_callback_list_t *list; 411 412 list = &fsh_global_callback_list; 413 414 rw_enter(&list->fshcl_lock, RW_WRITER); 415 node = list->fshcl_head; 416 417 if (node == NULL) { 418 rw_exit(&list->fshcl_lock); 419 return; 420 } 421 422 while (node && memcmp(fsh_callback, &node->fshcn_callback, 423 sizeof (*fsh_callback))) { 424 prev = node; 425 node = node->fshcn_next; 426 } 427 428 if (node == NULL) { 429 rw_exit(&list->fshcl_lock); 430 return; 431 } 432 433 prev->fshcn_next = node->fshcn_next; 434 kmem_free(node, sizeof (*node)); 435 436 rw_exit(&list->fshcl_lock); 437 } 438 439 /* 440 * This function is executed right before returning from domount()@vfs.c. 441 * We are sure that it's called only after fsh_init(). 442 * It does all the mount callbacks installed in the FSH. 443 */ 444 void 445 fsh_exec_mount_callbacks(vfs_t *vfsp) 446 { 447 fsh_callback_node_t *node; 448 fsh_callback_t *callback; 449 450 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER); 451 node = fsh_global_callback_list.fshcl_head; 452 while (node) { 453 callback = &node->fshcn_callback; 454 (*(callback->fshc_mount))(vfsp, callback->fshc_arg); 455 node = node->fshcn_next; 456 } 457 rw_exit(&fsh_global_callback_list.fshcl_lock); 458 } 459 460 /* 461 * This function is executed right before VFS_FREEVFS() is called in 462 * vfs_rele()@vfs.c. We are sure that it's called only after fsh_init(). 463 * It does all the free callbacks installed in the FSH. 464 */ 465 void 466 fsh_exec_free_callbacks(vfs_t *vfsp) 467 { 468 fsh_callback_node_t *node; 469 fsh_callback_t *callback; 470 471 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER); 472 node = fsh_global_callback_list.fshcl_head; 473 while (node) { 474 callback = &node->fshcn_callback; 475 (*(callback->fshc_free))(vfsp, callback->fshc_arg); 476 node = node->fshcn_next; 477 } 478 rw_exit(&fsh_global_callback_list.fshcl_lock); 479 } 480 481 /* 482 * API for vnode.c/vfs.c to start executing the FSH for a given operation. 483 * 484 * These interfaces are using fsh_res_ptr (in FSH_PREPARE_FSREC()), so it's 485 * absolutely necessary to call fsh_init() before using them. That's done in 486 * vfsinit(). 487 * 488 * While these functions are executing, it's expected that necessary vfs_t's 489 * are held so that vfs_free() isn't called. vfs_free() expects that noone 490 * else accesses vfs_fshrecord of a given vfs_t. 491 * It's also the caller responsibility to keep vnode_t passed to fsh_xxx() 492 * alive and valid. 493 * All these expectations are met because these functions are used only in 494 * correspondng fop/fsop_xxx(). 495 * 496 * Although fsrec->fshfsr_enabled is shared by everyone who uses a given 497 * vfs_t, it can be accessed in the usual way. There's no locking involved 498 * because all the changes of this field are made by atomic operations in 499 * fsh_fs_enable/disable(). 500 */ 501 int 502 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) 503 { 504 fsh_fsrecord_t *fsrec; 505 fsh_list_t *list; 506 int ret; 507 508 FSH_PREPARE_FSREC((*vpp)->v_vfsp); 509 fsrec = (*vpp)->v_vfsp->vfs_fshrecord; 510 if (!(fsrec->fshfsr_enabled)) 511 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct)); 512 513 list = &fsrec->fshfsr_opv[FSH_VOP_OPEN]; 514 rw_enter(&list->fshl_lock, RW_READER); 515 if (list->fshl_head == NULL) 516 ret = (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct); 517 else 518 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct); 519 rw_exit(&list->fshl_lock); 520 521 return (ret); 522 } 523 524 int 525 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, 526 caller_context_t *ct) 527 { 528 fsh_fsrecord_t *fsrec; 529 fsh_list_t *list; 530 int ret; 531 532 FSH_PREPARE_FSREC(vp->v_vfsp); 533 fsrec = vp->v_vfsp->vfs_fshrecord; 534 if (!(fsrec->fshfsr_enabled)) 535 return ((*(vp->v_op->vop_close))(vp, flag, count, offset, 536 cr, ct)); 537 538 list = &fsrec->fshfsr_opv[FSH_VOP_CLOSE]; 539 rw_enter(&list->fshl_lock, RW_READER); 540 if (list->fshl_head == NULL) 541 ret = (*(vp->v_op->vop_close))(vp, flag, count, offset, 542 cr, ct); 543 else 544 ret = fsh_next_close(list->fshl_head, vp, flag, count, 545 offset, cr, ct); 546 rw_exit(&list->fshl_lock); 547 548 return (ret); 549 } 550 551 int 552 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, 553 caller_context_t *ct) 554 { 555 fsh_fsrecord_t *fsrec; 556 fsh_list_t *list; 557 int ret; 558 559 FSH_PREPARE_FSREC(vp->v_vfsp); 560 fsrec = vp->v_vfsp->vfs_fshrecord; 561 if (!(fsrec->fshfsr_enabled)) 562 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct)); 563 564 list = &fsrec->fshfsr_opv[FSH_VOP_READ]; 565 rw_enter(&list->fshl_lock, RW_READER); 566 if (list->fshl_head == NULL) 567 ret = (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct); 568 else 569 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag, 570 cr, ct); 571 rw_exit(&list->fshl_lock); 572 573 return (ret); 574 } 575 576 int 577 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, 578 caller_context_t *ct) 579 { 580 fsh_fsrecord_t *fsrec; 581 fsh_list_t *list; 582 int ret; 583 584 FSH_PREPARE_FSREC(vp->v_vfsp); 585 fsrec = vp->v_vfsp->vfs_fshrecord; 586 if (!(fsrec->fshfsr_enabled)) 587 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct)); 588 589 list = &fsrec->fshfsr_opv[FSH_VOP_WRITE]; 590 rw_enter(&list->fshl_lock, RW_READER); 591 if (list->fshl_head == NULL) 592 ret = (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct); 593 else 594 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag, 595 cr, ct); 596 rw_exit(&list->fshl_lock); 597 598 return (ret); 599 } 600 601 int 602 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 603 { 604 fsh_fsrecord_t *fsrec; 605 fsh_list_t *list; 606 int ret; 607 608 FSH_PREPARE_FSREC(vfsp); 609 fsrec = vfsp->vfs_fshrecord; 610 if (!(fsrec->fshfsr_enabled)) 611 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr)); 612 613 list = &fsrec->fshfsr_opv[FSH_VFS_MOUNT]; 614 rw_enter(&list->fshl_lock, RW_READER); 615 if (list->fshl_head == NULL) 616 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr); 617 else 618 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap, 619 cr); 620 rw_exit(&list->fshl_lock); 621 622 return (ret); 623 } 624 625 int 626 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr) 627 { 628 fsh_fsrecord_t *fsrec; 629 fsh_list_t *list; 630 int ret; 631 632 FSH_PREPARE_FSREC(vfsp); 633 fsrec = vfsp->vfs_fshrecord; 634 if (!(fsrec->fshfsr_enabled)) 635 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr)); 636 637 list = &fsrec->fshfsr_opv[FSH_VFS_UNMOUNT]; 638 rw_enter(&list->fshl_lock, RW_READER); 639 if (list->fshl_head == NULL) 640 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr); 641 else 642 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr); 643 rw_exit(&list->fshl_lock); 644 645 return (ret); 646 } 647 648 int 649 fsh_root(vfs_t *vfsp, vnode_t **vpp) 650 { 651 fsh_fsrecord_t *fsrec; 652 fsh_list_t *list; 653 int ret; 654 655 FSH_PREPARE_FSREC(vfsp); 656 fsrec = vfsp->vfs_fshrecord; 657 if (!(fsrec->fshfsr_enabled)) 658 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp)); 659 660 list = &fsrec->fshfsr_opv[FSH_VFS_ROOT]; 661 rw_enter(&list->fshl_lock, RW_READER); 662 if (list->fshl_head == NULL) 663 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp); 664 else 665 ret = fsh_next_root(list->fshl_head, vfsp, vpp); 666 rw_exit(&list->fshl_lock); 667 668 return (ret); 669 } 670 671 int 672 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp) 673 { 674 fsh_fsrecord_t *fsrec; 675 fsh_list_t *list; 676 int ret; 677 678 FSH_PREPARE_FSREC(vfsp); 679 fsrec = vfsp->vfs_fshrecord; 680 if (!(fsrec->fshfsr_enabled)) 681 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp)); 682 683 list = &fsrec->fshfsr_opv[FSH_VFS_STATFS]; 684 rw_enter(&list->fshl_lock, RW_READER); 685 if (list->fshl_head == NULL) 686 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp); 687 else 688 ret = fsh_next_statfs(list->fshl_head, vfsp, sp); 689 rw_exit(&list->fshl_lock); 690 691 return (ret); 692 } 693 694 int 695 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) 696 { 697 fsh_fsrecord_t *fsrec; 698 fsh_list_t *list; 699 int ret; 700 701 FSH_PREPARE_FSREC(vfsp); 702 fsrec = vfsp->vfs_fshrecord; 703 if (!(fsrec->fshfsr_enabled)) 704 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp)); 705 706 list = &fsrec->fshfsr_opv[FSH_VFS_VGET]; 707 rw_enter(&list->fshl_lock, RW_READER); 708 if (list->fshl_head == NULL) 709 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp); 710 else 711 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp); 712 rw_exit(&list->fshl_lock); 713 714 return (ret); 715 } 716 717 /* 718 * This is the funtion used by FSH_PREPARE_FSREC() to allocate a new 719 * fsh_fsrecord. This function is called by the first function which 720 * access the vfs_fshrecord and finds out it's NULL. 721 */ 722 static struct fsh_fsrecord * 723 fsh_fsrec_create() 724 { 725 struct fsh_fsrecord *fsrecp; 726 int i; 727 728 fsrecp = (struct fsh_fsrecord *)kmem_zalloc(sizeof (*fsrecp), 729 KM_SLEEP); 730 fsrecp->fshfsr_enabled = 1; 731 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) 732 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER, 733 NULL); 734 return (fsrecp); 735 } 736 737 /* 738 * This call can be used ONLY in vfs_free(). It's assumed that no other 739 * fsh_xxx() using the vfs_t that owns the fsh_fsrecord to be destroyed 740 * is executing while call to fsh_fsrec_destroy() is made. With this 741 * assumptions, no concurrency issues occur. All rwlocks of fshfsr_opv 742 * elements are released. 743 */ 744 void 745 fsh_fsrec_destroy(struct fsh_fsrecord *volatile fsrecp) 746 { 747 int i; 748 fsh_node_t *node, *next_node; 749 750 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) { 751 node = fsrecp->fshfsr_opv[i].fshl_head; 752 while (node) { 753 next_node = node->fshn_next; 754 kmem_free(node, sizeof (*node)); 755 node = next_node; 756 } 757 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock); 758 } 759 kmem_free(fsrecp, sizeof (*fsrecp)); 760 } 761 762 /* 763 * fsh_init() is called in vfsinit()@vfs.c. This function MUST be called 764 * before every other FSH call. 765 */ 766 void 767 fsh_init(void) 768 { 769 rw_init(&fsh_global_callback_list.fshcl_lock, NULL, RW_DRIVER, NULL); 770 fsh_res_ptr = kmem_alloc(1, KM_SLEEP); 771 } 772 773 /* 774 * These functions are used to pass control to the next hook or underlying 775 * vop/vfsop. It's consumer doesn't have to worry about any locking, because 776 * all the necessities are guaranteed by the fsh_ 777 */ 778 int 779 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr, 780 caller_context_t *ct) 781 { 782 if (fsh_node == NULL) 783 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct)); 784 785 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))( 786 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 787 vpp, mode, cr, ct)); 788 } 789 790 int 791 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count, 792 offset_t offset, cred_t *cr, caller_context_t *ct) 793 { 794 if (fsh_node == NULL) 795 return ((*(vp->v_op->vop_close))(vp, flag, count, offset, 796 cr, ct)); 797 798 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))( 799 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 800 vp, flag, count, offset, cr, ct)); 801 } 802 803 int 804 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag, 805 cred_t *cr, caller_context_t *ct) 806 { 807 if (fsh_node == NULL) 808 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct)); 809 810 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))( 811 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 812 vp, uiop, ioflag, cr, ct)); 813 } 814 815 int 816 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag, 817 cred_t *cr, caller_context_t *ct) 818 { 819 if (fsh_node == NULL) 820 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct)); 821 822 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))( 823 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 824 vp, uiop, ioflag, cr, ct)); 825 } 826 827 int 828 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp, 829 struct mounta *uap, cred_t *cr) 830 { 831 if (fsh_node == NULL) 832 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr)); 833 834 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))( 835 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 836 vfsp, mvp, uap, cr)); 837 } 838 839 int 840 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr) 841 { 842 if (fsh_node == NULL) 843 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr)); 844 845 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))( 846 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 847 vfsp, flag, cr)); 848 } 849 850 int 851 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp) 852 { 853 if (fsh_node == NULL) 854 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp)); 855 856 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))( 857 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 858 vfsp, vpp)); 859 } 860 861 int 862 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp) 863 { 864 if (fsh_node == NULL) 865 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp)); 866 867 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))( 868 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 869 vfsp, sp)); 870 } 871 872 int 873 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) 874 { 875 if (fsh_node == NULL) 876 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp)); 877 878 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))( 879 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg, 880 vfsp, vpp, fidp)); 881 }