1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 22 /* All Rights Reserved */ 23 24 25 /* 26 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 /* 31 * Generic vnode operations. 32 */ 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/errno.h> 37 #include <sys/fcntl.h> 38 #include <sys/flock.h> 39 #include <sys/statvfs.h> 40 #include <sys/vfs.h> 41 #include <sys/vnode.h> 42 #include <sys/proc.h> 43 #include <sys/user.h> 44 #include <sys/unistd.h> 45 #include <sys/cred.h> 46 #include <sys/poll.h> 47 #include <sys/debug.h> 48 #include <sys/cmn_err.h> 49 #include <sys/stream.h> 50 #include <fs/fs_subr.h> 51 #include <sys/acl.h> 52 #include <sys/share.h> 53 #include <sys/file.h> 54 #include <sys/kmem.h> 55 #include <sys/file.h> 56 #include <sys/nbmlock.h> 57 #include <acl/acl_common.h> 58 59 static callb_cpr_t *frlock_serialize_blocked(flk_cb_when_t, void *); 60 61 /* 62 * Tunable to limit the number of retry to recover from STALE error. 63 */ 64 int fs_estale_retry = 5; 65 66 /* 67 * The associated operation is not supported by the file system. 68 */ 69 int 70 fs_nosys() 71 { 72 return (ENOSYS); 73 } 74 75 /* 76 * The associated operation is invalid (on this vnode). 77 */ 78 int 79 fs_inval() 80 { 81 return (EINVAL); 82 } 83 84 /* 85 * The associated operation is valid only for directories. 86 */ 87 int 88 fs_notdir() 89 { 90 return (ENOTDIR); 91 } 92 93 /* 94 * Free the file system specific resources. For the file systems that 95 * do not support the forced unmount, it will be a nop function. 96 */ 97 98 /*ARGSUSED*/ 99 void 100 fs_freevfs(vfs_t *vfsp) 101 { 102 } 103 104 /* ARGSUSED */ 105 int 106 fs_nosys_map(struct vnode *vp, 107 offset_t off, 108 struct as *as, 109 caddr_t *addrp, 110 size_t len, 111 uchar_t prot, 112 uchar_t maxprot, 113 uint_t flags, 114 struct cred *cr, 115 caller_context_t *ct) 116 { 117 return (ENOSYS); 118 } 119 120 /* ARGSUSED */ 121 int 122 fs_nosys_addmap(struct vnode *vp, 123 offset_t off, 124 struct as *as, 125 caddr_t addr, 126 size_t len, 127 uchar_t prot, 128 uchar_t maxprot, 129 uint_t flags, 130 struct cred *cr, 131 caller_context_t *ct) 132 { 133 return (ENOSYS); 134 } 135 136 /* ARGSUSED */ 137 int 138 fs_nosys_poll(vnode_t *vp, 139 register short events, 140 int anyyet, 141 register short *reventsp, 142 struct pollhead **phpp, 143 caller_context_t *ct) 144 { 145 return (ENOSYS); 146 } 147 148 149 /* 150 * The file system has nothing to sync to disk. However, the 151 * VFS_SYNC operation must not fail. 152 */ 153 /* ARGSUSED */ 154 int 155 fs_sync(struct vfs *vfspp, short flag, cred_t *cr) 156 { 157 return (0); 158 } 159 160 /* 161 * Does nothing but VOP_FSYNC must not fail. 162 */ 163 /* ARGSUSED */ 164 int 165 fs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) 166 { 167 return (0); 168 } 169 170 /* 171 * Does nothing but VOP_PUTPAGE must not fail. 172 */ 173 /* ARGSUSED */ 174 int 175 fs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, 176 caller_context_t *ctp) 177 { 178 return (0); 179 } 180 181 /* 182 * Does nothing but VOP_IOCTL must not fail. 183 */ 184 /* ARGSUSED */ 185 int 186 fs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred, 187 int *rvalp) 188 { 189 return (0); 190 } 191 192 /* 193 * Read/write lock/unlock. Does nothing. 194 */ 195 /* ARGSUSED */ 196 int 197 fs_rwlock(vnode_t *vp, int write_lock, caller_context_t *ctp) 198 { 199 return (-1); 200 } 201 202 /* ARGSUSED */ 203 void 204 fs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) 205 { 206 } 207 208 /* 209 * Compare two vnodes. 210 */ 211 /*ARGSUSED2*/ 212 int 213 fs_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) 214 { 215 return (vp1 == vp2); 216 } 217 218 /* 219 * No-op seek operation. 220 */ 221 /* ARGSUSED */ 222 int 223 fs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) 224 { 225 return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); 226 } 227 228 /* 229 * File and record locking. 230 */ 231 /* ARGSUSED */ 232 int 233 fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, 234 offset_t offset, flk_callback_t *flk_cbp, cred_t *cr, 235 caller_context_t *ct) 236 { 237 int frcmd; 238 int nlmid; 239 int error = 0; 240 flk_callback_t serialize_callback; 241 int serialize = 0; 242 v_mode_t mode; 243 244 switch (cmd) { 245 246 case F_GETLK: 247 case F_O_GETLK: 248 if (flag & F_REMOTELOCK) { 249 frcmd = RCMDLCK; 250 } else if (flag & F_PXFSLOCK) { 251 frcmd = PCMDLCK; 252 } else { 253 frcmd = 0; 254 bfp->l_pid = ttoproc(curthread)->p_pid; 255 bfp->l_sysid = 0; 256 } 257 break; 258 259 case F_SETLK_NBMAND: 260 /* 261 * Are NBMAND locks allowed on this file? 262 */ 263 if (!vp->v_vfsp || 264 !(vp->v_vfsp->vfs_flag & VFS_NBMAND)) { 265 error = EINVAL; 266 goto done; 267 } 268 if (vp->v_type != VREG) { 269 error = EINVAL; 270 goto done; 271 } 272 /*FALLTHROUGH*/ 273 274 case F_SETLK: 275 if (flag & F_REMOTELOCK) { 276 frcmd = SETFLCK|RCMDLCK; 277 } else if (flag & F_PXFSLOCK) { 278 frcmd = SETFLCK|PCMDLCK; 279 } else { 280 frcmd = SETFLCK; 281 bfp->l_pid = ttoproc(curthread)->p_pid; 282 bfp->l_sysid = 0; 283 } 284 if (cmd == F_SETLK_NBMAND && 285 (bfp->l_type == F_RDLCK || bfp->l_type == F_WRLCK)) { 286 frcmd |= NBMLCK; 287 } 288 289 if (nbl_need_check(vp)) { 290 nbl_start_crit(vp, RW_WRITER); 291 serialize = 1; 292 if (frcmd & NBMLCK) { 293 mode = (bfp->l_type == F_RDLCK) ? 294 V_READ : V_RDANDWR; 295 if (vn_is_mapped(vp, mode)) { 296 error = EAGAIN; 297 goto done; 298 } 299 } 300 } 301 break; 302 303 case F_SETLKW: 304 if (flag & F_REMOTELOCK) { 305 frcmd = SETFLCK|SLPFLCK|RCMDLCK; 306 } else if (flag & F_PXFSLOCK) { 307 frcmd = SETFLCK|SLPFLCK|PCMDLCK; 308 } else { 309 frcmd = SETFLCK|SLPFLCK; 310 bfp->l_pid = ttoproc(curthread)->p_pid; 311 bfp->l_sysid = 0; 312 } 313 314 if (nbl_need_check(vp)) { 315 nbl_start_crit(vp, RW_WRITER); 316 serialize = 1; 317 } 318 break; 319 320 case F_HASREMOTELOCKS: 321 nlmid = GETNLMID(bfp->l_sysid); 322 if (nlmid != 0) { /* booted as a cluster */ 323 l_has_rmt(bfp) = 324 cl_flk_has_remote_locks_for_nlmid(vp, nlmid); 325 } else { /* not booted as a cluster */ 326 l_has_rmt(bfp) = flk_has_remote_locks(vp); 327 } 328 329 goto done; 330 331 default: 332 error = EINVAL; 333 goto done; 334 } 335 336 /* 337 * If this is a blocking lock request and we're serializing lock 338 * requests, modify the callback list to leave the critical region 339 * while we're waiting for the lock. 340 */ 341 342 if (serialize && (frcmd & SLPFLCK) != 0) { 343 flk_add_callback(&serialize_callback, 344 frlock_serialize_blocked, vp, flk_cbp); 345 flk_cbp = &serialize_callback; 346 } 347 348 error = reclock(vp, bfp, frcmd, flag, offset, flk_cbp); 349 350 done: 351 if (serialize) 352 nbl_end_crit(vp); 353 354 return (error); 355 } 356 357 /* 358 * Callback when a lock request blocks and we are serializing requests. If 359 * before sleeping, leave the critical region. If after wakeup, reenter 360 * the critical region. 361 */ 362 363 static callb_cpr_t * 364 frlock_serialize_blocked(flk_cb_when_t when, void *infop) 365 { 366 vnode_t *vp = (vnode_t *)infop; 367 368 if (when == FLK_BEFORE_SLEEP) 369 nbl_end_crit(vp); 370 else { 371 nbl_start_crit(vp, RW_WRITER); 372 } 373 374 return (NULL); 375 } 376 377 /* 378 * Allow any flags. 379 */ 380 /* ARGSUSED */ 381 int 382 fs_setfl( 383 vnode_t *vp, 384 int oflags, 385 int nflags, 386 cred_t *cr, 387 caller_context_t *ct) 388 { 389 return (0); 390 } 391 392 /* 393 * Return the answer requested to poll() for non-device files. 394 * Only POLLIN, POLLRDNORM, and POLLOUT are recognized. 395 */ 396 struct pollhead fs_pollhd; 397 398 /* ARGSUSED */ 399 int 400 fs_poll(vnode_t *vp, 401 register short events, 402 int anyyet, 403 register short *reventsp, 404 struct pollhead **phpp, 405 caller_context_t *ct) 406 { 407 *reventsp = 0; 408 if (events & POLLIN) 409 *reventsp |= POLLIN; 410 if (events & POLLRDNORM) 411 *reventsp |= POLLRDNORM; 412 if (events & POLLRDBAND) 413 *reventsp |= POLLRDBAND; 414 if (events & POLLOUT) 415 *reventsp |= POLLOUT; 416 if (events & POLLWRBAND) 417 *reventsp |= POLLWRBAND; 418 *phpp = !anyyet && !*reventsp ? &fs_pollhd : (struct pollhead *)NULL; 419 return (0); 420 } 421 422 /* 423 * POSIX pathconf() support. 424 */ 425 /* ARGSUSED */ 426 int 427 fs_pathconf( 428 vnode_t *vp, 429 int cmd, 430 ulong_t *valp, 431 cred_t *cr, 432 caller_context_t *ct) 433 { 434 register ulong_t val; 435 register int error = 0; 436 struct statvfs64 vfsbuf; 437 438 switch (cmd) { 439 440 case _PC_LINK_MAX: 441 val = MAXLINK; 442 break; 443 444 case _PC_MAX_CANON: 445 val = MAX_CANON; 446 break; 447 448 case _PC_MAX_INPUT: 449 val = MAX_INPUT; 450 break; 451 452 case _PC_NAME_MAX: 453 bzero(&vfsbuf, sizeof (vfsbuf)); 454 if (error = VFS_STATVFS(vp->v_vfsp, &vfsbuf)) 455 break; 456 val = vfsbuf.f_namemax; 457 break; 458 459 case _PC_PATH_MAX: 460 case _PC_SYMLINK_MAX: 461 val = MAXPATHLEN; 462 break; 463 464 case _PC_PIPE_BUF: 465 val = PIPE_BUF; 466 break; 467 468 case _PC_NO_TRUNC: 469 if (vp->v_vfsp->vfs_flag & VFS_NOTRUNC) 470 val = 1; /* NOTRUNC is enabled for vp */ 471 else 472 val = (ulong_t)-1; 473 break; 474 475 case _PC_VDISABLE: 476 val = _POSIX_VDISABLE; 477 break; 478 479 case _PC_CHOWN_RESTRICTED: 480 if (rstchown) 481 val = rstchown; /* chown restricted enabled */ 482 else 483 val = (ulong_t)-1; 484 break; 485 486 case _PC_FILESIZEBITS: 487 488 /* 489 * If ever we come here it means that underlying file system 490 * does not recognise the command and therefore this 491 * configurable limit cannot be determined. We return -1 492 * and don't change errno. 493 */ 494 495 val = (ulong_t)-1; /* large file support */ 496 break; 497 498 case _PC_ACL_ENABLED: 499 val = 0; 500 break; 501 502 case _PC_CASE_BEHAVIOR: 503 val = _CASE_SENSITIVE; 504 if (vfs_has_feature(vp->v_vfsp, VFSFT_CASEINSENSITIVE) == 1) 505 val |= _CASE_INSENSITIVE; 506 if (vfs_has_feature(vp->v_vfsp, VFSFT_NOCASESENSITIVE) == 1) 507 val &= ~_CASE_SENSITIVE; 508 break; 509 510 case _PC_SATTR_ENABLED: 511 case _PC_SATTR_EXISTS: 512 val = 0; 513 break; 514 515 case _PC_ACCESS_FILTERING: 516 val = 0; 517 break; 518 519 default: 520 error = EINVAL; 521 break; 522 } 523 524 if (error == 0) 525 *valp = val; 526 return (error); 527 } 528 529 /* 530 * Dispose of a page. 531 */ 532 /* ARGSUSED */ 533 void 534 fs_dispose( 535 struct vnode *vp, 536 page_t *pp, 537 int fl, 538 int dn, 539 struct cred *cr, 540 caller_context_t *ct) 541 { 542 543 ASSERT(fl == B_FREE || fl == B_INVAL); 544 545 if (fl == B_FREE) 546 page_free(pp, dn); 547 else 548 page_destroy(pp, dn); 549 } 550 551 /* ARGSUSED */ 552 void 553 fs_nodispose( 554 struct vnode *vp, 555 page_t *pp, 556 int fl, 557 int dn, 558 struct cred *cr, 559 caller_context_t *ct) 560 { 561 cmn_err(CE_PANIC, "fs_nodispose invoked"); 562 } 563 564 /* 565 * fabricate acls for file systems that do not support acls. 566 */ 567 /* ARGSUSED */ 568 int 569 fs_fab_acl( 570 vnode_t *vp, 571 vsecattr_t *vsecattr, 572 int flag, 573 cred_t *cr, 574 caller_context_t *ct) 575 { 576 aclent_t *aclentp; 577 ace_t *acep; 578 struct vattr vattr; 579 int error; 580 size_t aclsize; 581 582 vsecattr->vsa_aclcnt = 0; 583 vsecattr->vsa_aclentsz = 0; 584 vsecattr->vsa_aclentp = NULL; 585 vsecattr->vsa_dfaclcnt = 0; /* Default ACLs are not fabricated */ 586 vsecattr->vsa_dfaclentp = NULL; 587 588 vattr.va_mask = AT_MODE | AT_UID | AT_GID; 589 if (error = VOP_GETATTR(vp, &vattr, 0, cr, ct)) 590 return (error); 591 592 if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) { 593 aclsize = 4 * sizeof (aclent_t); 594 vsecattr->vsa_aclcnt = 4; /* USER, GROUP, OTHER, and CLASS */ 595 vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); 596 aclentp = vsecattr->vsa_aclentp; 597 598 aclentp->a_type = USER_OBJ; /* Owner */ 599 aclentp->a_perm = ((ushort_t)(vattr.va_mode & 0700)) >> 6; 600 aclentp->a_id = vattr.va_uid; /* Really undefined */ 601 aclentp++; 602 603 aclentp->a_type = GROUP_OBJ; /* Group */ 604 aclentp->a_perm = ((ushort_t)(vattr.va_mode & 0070)) >> 3; 605 aclentp->a_id = vattr.va_gid; /* Really undefined */ 606 aclentp++; 607 608 aclentp->a_type = OTHER_OBJ; /* Other */ 609 aclentp->a_perm = vattr.va_mode & 0007; 610 aclentp->a_id = (gid_t)-1; /* Really undefined */ 611 aclentp++; 612 613 aclentp->a_type = CLASS_OBJ; /* Class */ 614 aclentp->a_perm = (ushort_t)(0007); 615 aclentp->a_id = (gid_t)-1; /* Really undefined */ 616 } else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) { 617 aclsize = 6 * sizeof (ace_t); 618 vsecattr->vsa_aclcnt = 6; 619 vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); 620 vsecattr->vsa_aclentsz = aclsize; 621 acep = vsecattr->vsa_aclentp; 622 (void) memcpy(acep, trivial_acl, sizeof (ace_t) * 6); 623 adjust_ace_pair(acep, (vattr.va_mode & 0700) >> 6); 624 adjust_ace_pair(acep + 2, (vattr.va_mode & 0070) >> 3); 625 adjust_ace_pair(acep + 4, vattr.va_mode & 0007); 626 } 627 628 return (0); 629 } 630 631 /* 632 * Common code for implementing DOS share reservations 633 */ 634 /* ARGSUSED4 */ 635 int 636 fs_shrlock( 637 struct vnode *vp, 638 int cmd, 639 struct shrlock *shr, 640 int flag, 641 cred_t *cr, 642 caller_context_t *ct) 643 { 644 int error; 645 646 /* 647 * Make sure that the file was opened with permissions appropriate 648 * for the request, and make sure the caller isn't trying to sneak 649 * in an NBMAND request. 650 */ 651 if (cmd == F_SHARE) { 652 if (((shr->s_access & F_RDACC) && (flag & FREAD) == 0) || 653 ((shr->s_access & F_WRACC) && (flag & FWRITE) == 0)) 654 return (EBADF); 655 if (shr->s_access & (F_RMACC | F_MDACC)) 656 return (EINVAL); 657 if (shr->s_deny & (F_MANDDNY | F_RMDNY)) 658 return (EINVAL); 659 } 660 if (cmd == F_SHARE_NBMAND) { 661 /* make sure nbmand is allowed on the file */ 662 if (!vp->v_vfsp || 663 !(vp->v_vfsp->vfs_flag & VFS_NBMAND)) { 664 return (EINVAL); 665 } 666 if (vp->v_type != VREG) { 667 return (EINVAL); 668 } 669 } 670 671 nbl_start_crit(vp, RW_WRITER); 672 673 switch (cmd) { 674 675 case F_SHARE_NBMAND: 676 shr->s_deny |= F_MANDDNY; 677 /*FALLTHROUGH*/ 678 case F_SHARE: 679 error = add_share(vp, shr); 680 break; 681 682 case F_UNSHARE: 683 error = del_share(vp, shr); 684 break; 685 686 case F_HASREMOTELOCKS: 687 /* 688 * We are overloading this command to refer to remote 689 * shares as well as remote locks, despite its name. 690 */ 691 shr->s_access = shr_has_remote_shares(vp, shr->s_sysid); 692 error = 0; 693 break; 694 695 default: 696 error = EINVAL; 697 break; 698 } 699 700 nbl_end_crit(vp); 701 return (error); 702 } 703 704 /*ARGSUSED1*/ 705 int 706 fs_vnevent_nosupport(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, 707 caller_context_t *ct) 708 { 709 ASSERT(vp != NULL); 710 return (ENOTSUP); 711 } 712 713 /*ARGSUSED1*/ 714 int 715 fs_vnevent_support(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, 716 caller_context_t *ct) 717 { 718 ASSERT(vp != NULL); 719 return (0); 720 } 721 722 /* 723 * return 1 for non-trivial ACL. 724 * 725 * NB: It is not necessary for the caller to VOP_RWLOCK since 726 * we only issue VOP_GETSECATTR. 727 * 728 * Returns 0 == trivial 729 * 1 == NOT Trivial 730 * <0 could not determine. 731 */ 732 int 733 fs_acl_nontrivial(vnode_t *vp, cred_t *cr) 734 { 735 ulong_t acl_styles; 736 ulong_t acl_flavor; 737 vsecattr_t vsecattr; 738 int error; 739 int isnontrivial; 740 741 /* determine the forms of ACLs maintained */ 742 error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &acl_styles, cr, NULL); 743 744 /* clear bits we don't understand and establish default acl_style */ 745 acl_styles &= (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED); 746 if (error || (acl_styles == 0)) 747 acl_styles = _ACL_ACLENT_ENABLED; 748 749 vsecattr.vsa_aclentp = NULL; 750 vsecattr.vsa_dfaclentp = NULL; 751 vsecattr.vsa_aclcnt = 0; 752 vsecattr.vsa_dfaclcnt = 0; 753 754 while (acl_styles) { 755 /* select one of the styles as current flavor */ 756 acl_flavor = 0; 757 if (acl_styles & _ACL_ACLENT_ENABLED) { 758 acl_flavor = _ACL_ACLENT_ENABLED; 759 vsecattr.vsa_mask = VSA_ACLCNT | VSA_DFACLCNT; 760 } else if (acl_styles & _ACL_ACE_ENABLED) { 761 acl_flavor = _ACL_ACE_ENABLED; 762 vsecattr.vsa_mask = VSA_ACECNT | VSA_ACE; 763 } 764 765 ASSERT(vsecattr.vsa_mask && acl_flavor); 766 error = VOP_GETSECATTR(vp, &vsecattr, 0, cr, NULL); 767 if (error == 0) 768 break; 769 770 /* that flavor failed */ 771 acl_styles &= ~acl_flavor; 772 } 773 774 /* if all styles fail then assume trivial */ 775 if (acl_styles == 0) 776 return (0); 777 778 /* process the flavor that worked */ 779 isnontrivial = 0; 780 if (acl_flavor & _ACL_ACLENT_ENABLED) { 781 if (vsecattr.vsa_aclcnt > MIN_ACL_ENTRIES) 782 isnontrivial = 1; 783 if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp != NULL) 784 kmem_free(vsecattr.vsa_aclentp, 785 vsecattr.vsa_aclcnt * sizeof (aclent_t)); 786 if (vsecattr.vsa_dfaclcnt && vsecattr.vsa_dfaclentp != NULL) 787 kmem_free(vsecattr.vsa_dfaclentp, 788 vsecattr.vsa_dfaclcnt * sizeof (aclent_t)); 789 } 790 if (acl_flavor & _ACL_ACE_ENABLED) { 791 792 isnontrivial = ace_trivial(vsecattr.vsa_aclentp, 793 vsecattr.vsa_aclcnt); 794 795 if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp != NULL) 796 kmem_free(vsecattr.vsa_aclentp, 797 vsecattr.vsa_aclcnt * sizeof (ace_t)); 798 /* ACE has no vsecattr.vsa_dfaclcnt */ 799 } 800 return (isnontrivial); 801 } 802 803 /* 804 * Check whether we need a retry to recover from STALE error. 805 */ 806 int 807 fs_need_estale_retry(int retry_count) 808 { 809 if (retry_count < fs_estale_retry) 810 return (1); 811 else 812 return (0); 813 } 814 815 816 static int (*fs_av_scan)(vnode_t *, cred_t *, int) = NULL; 817 818 /* 819 * Routine for anti-virus scanner to call to register its scanning routine. 820 */ 821 void 822 fs_vscan_register(int (*av_scan)(vnode_t *, cred_t *, int)) 823 { 824 fs_av_scan = av_scan; 825 } 826 827 /* 828 * Routine for file systems to call to initiate anti-virus scanning. 829 * Scanning will only be done on REGular files (currently). 830 */ 831 int 832 fs_vscan(vnode_t *vp, cred_t *cr, int async) 833 { 834 int ret = 0; 835 836 if (fs_av_scan && vp->v_type == VREG) 837 ret = (*fs_av_scan)(vp, cr, async); 838 839 return (ret); 840 } 841