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 2008 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 default: 516 error = EINVAL; 517 break; 518 } 519 520 if (error == 0) 521 *valp = val; 522 return (error); 523 } 524 525 /* 526 * Dispose of a page. 527 */ 528 /* ARGSUSED */ 529 void 530 fs_dispose( 531 struct vnode *vp, 532 page_t *pp, 533 int fl, 534 int dn, 535 struct cred *cr, 536 caller_context_t *ct) 537 { 538 539 ASSERT(fl == B_FREE || fl == B_INVAL); 540 541 if (fl == B_FREE) 542 page_free(pp, dn); 543 else 544 page_destroy(pp, dn); 545 } 546 547 /* ARGSUSED */ 548 void 549 fs_nodispose( 550 struct vnode *vp, 551 page_t *pp, 552 int fl, 553 int dn, 554 struct cred *cr, 555 caller_context_t *ct) 556 { 557 cmn_err(CE_PANIC, "fs_nodispose invoked"); 558 } 559 560 /* 561 * fabricate acls for file systems that do not support acls. 562 */ 563 /* ARGSUSED */ 564 int 565 fs_fab_acl( 566 vnode_t *vp, 567 vsecattr_t *vsecattr, 568 int flag, 569 cred_t *cr, 570 caller_context_t *ct) 571 { 572 aclent_t *aclentp; 573 ace_t *acep; 574 struct vattr vattr; 575 int error; 576 size_t aclsize; 577 578 vsecattr->vsa_aclcnt = 0; 579 vsecattr->vsa_aclentsz = 0; 580 vsecattr->vsa_aclentp = NULL; 581 vsecattr->vsa_dfaclcnt = 0; /* Default ACLs are not fabricated */ 582 vsecattr->vsa_dfaclentp = NULL; 583 584 vattr.va_mask = AT_MODE | AT_UID | AT_GID; 585 if (error = VOP_GETATTR(vp, &vattr, 0, cr, ct)) 586 return (error); 587 588 if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) { 589 aclsize = 4 * sizeof (aclent_t); 590 vsecattr->vsa_aclcnt = 4; /* USER, GROUP, OTHER, and CLASS */ 591 vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); 592 aclentp = vsecattr->vsa_aclentp; 593 594 aclentp->a_type = USER_OBJ; /* Owner */ 595 aclentp->a_perm = ((ushort_t)(vattr.va_mode & 0700)) >> 6; 596 aclentp->a_id = vattr.va_uid; /* Really undefined */ 597 aclentp++; 598 599 aclentp->a_type = GROUP_OBJ; /* Group */ 600 aclentp->a_perm = ((ushort_t)(vattr.va_mode & 0070)) >> 3; 601 aclentp->a_id = vattr.va_gid; /* Really undefined */ 602 aclentp++; 603 604 aclentp->a_type = OTHER_OBJ; /* Other */ 605 aclentp->a_perm = vattr.va_mode & 0007; 606 aclentp->a_id = (gid_t)-1; /* Really undefined */ 607 aclentp++; 608 609 aclentp->a_type = CLASS_OBJ; /* Class */ 610 aclentp->a_perm = (ushort_t)(0007); 611 aclentp->a_id = (gid_t)-1; /* Really undefined */ 612 } else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) { 613 aclsize = 6 * sizeof (ace_t); 614 vsecattr->vsa_aclcnt = 6; 615 vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); 616 vsecattr->vsa_aclentsz = aclsize; 617 acep = vsecattr->vsa_aclentp; 618 (void) memcpy(acep, trivial_acl, sizeof (ace_t) * 6); 619 adjust_ace_pair(acep, (vattr.va_mode & 0700) >> 6); 620 adjust_ace_pair(acep + 2, (vattr.va_mode & 0070) >> 3); 621 adjust_ace_pair(acep + 4, vattr.va_mode & 0007); 622 } 623 624 return (0); 625 } 626 627 /* 628 * Common code for implementing DOS share reservations 629 */ 630 /* ARGSUSED4 */ 631 int 632 fs_shrlock( 633 struct vnode *vp, 634 int cmd, 635 struct shrlock *shr, 636 int flag, 637 cred_t *cr, 638 caller_context_t *ct) 639 { 640 int error; 641 642 /* 643 * Make sure that the file was opened with permissions appropriate 644 * for the request, and make sure the caller isn't trying to sneak 645 * in an NBMAND request. 646 */ 647 if (cmd == F_SHARE) { 648 if (((shr->s_access & F_RDACC) && (flag & FREAD) == 0) || 649 ((shr->s_access & F_WRACC) && (flag & FWRITE) == 0)) 650 return (EBADF); 651 if (shr->s_access & (F_RMACC | F_MDACC)) 652 return (EINVAL); 653 if (shr->s_deny & (F_MANDDNY | F_RMDNY)) 654 return (EINVAL); 655 } 656 if (cmd == F_SHARE_NBMAND) { 657 /* make sure nbmand is allowed on the file */ 658 if (!vp->v_vfsp || 659 !(vp->v_vfsp->vfs_flag & VFS_NBMAND)) { 660 return (EINVAL); 661 } 662 if (vp->v_type != VREG) { 663 return (EINVAL); 664 } 665 } 666 667 nbl_start_crit(vp, RW_WRITER); 668 669 switch (cmd) { 670 671 case F_SHARE_NBMAND: 672 shr->s_deny |= F_MANDDNY; 673 /*FALLTHROUGH*/ 674 case F_SHARE: 675 error = add_share(vp, shr); 676 break; 677 678 case F_UNSHARE: 679 error = del_share(vp, shr); 680 break; 681 682 case F_HASREMOTELOCKS: 683 /* 684 * We are overloading this command to refer to remote 685 * shares as well as remote locks, despite its name. 686 */ 687 shr->s_access = shr_has_remote_shares(vp, shr->s_sysid); 688 error = 0; 689 break; 690 691 default: 692 error = EINVAL; 693 break; 694 } 695 696 nbl_end_crit(vp); 697 return (error); 698 } 699 700 /*ARGSUSED1*/ 701 int 702 fs_vnevent_nosupport(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, 703 caller_context_t *ct) 704 { 705 ASSERT(vp != NULL); 706 return (ENOTSUP); 707 } 708 709 /*ARGSUSED1*/ 710 int 711 fs_vnevent_support(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, 712 caller_context_t *ct) 713 { 714 ASSERT(vp != NULL); 715 return (0); 716 } 717 718 /* 719 * return 1 for non-trivial ACL. 720 * 721 * NB: It is not necessary for the caller to VOP_RWLOCK since 722 * we only issue VOP_GETSECATTR. 723 * 724 * Returns 0 == trivial 725 * 1 == NOT Trivial 726 * <0 could not determine. 727 */ 728 int 729 fs_acl_nontrivial(vnode_t *vp, cred_t *cr) 730 { 731 ulong_t acl_styles; 732 ulong_t acl_flavor; 733 vsecattr_t vsecattr; 734 int error; 735 int isnontrivial; 736 737 /* determine the forms of ACLs maintained */ 738 error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &acl_styles, cr, NULL); 739 740 /* clear bits we don't understand and establish default acl_style */ 741 acl_styles &= (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED); 742 if (error || (acl_styles == 0)) 743 acl_styles = _ACL_ACLENT_ENABLED; 744 745 vsecattr.vsa_aclentp = NULL; 746 vsecattr.vsa_dfaclentp = NULL; 747 vsecattr.vsa_aclcnt = 0; 748 vsecattr.vsa_dfaclcnt = 0; 749 750 while (acl_styles) { 751 /* select one of the styles as current flavor */ 752 acl_flavor = 0; 753 if (acl_styles & _ACL_ACLENT_ENABLED) { 754 acl_flavor = _ACL_ACLENT_ENABLED; 755 vsecattr.vsa_mask = VSA_ACLCNT | VSA_DFACLCNT; 756 } else if (acl_styles & _ACL_ACE_ENABLED) { 757 acl_flavor = _ACL_ACE_ENABLED; 758 vsecattr.vsa_mask = VSA_ACECNT | VSA_ACE; 759 } 760 761 ASSERT(vsecattr.vsa_mask && acl_flavor); 762 error = VOP_GETSECATTR(vp, &vsecattr, 0, cr, NULL); 763 if (error == 0) 764 break; 765 766 /* that flavor failed */ 767 acl_styles &= ~acl_flavor; 768 } 769 770 /* if all styles fail then assume trivial */ 771 if (acl_styles == 0) 772 return (0); 773 774 /* process the flavor that worked */ 775 isnontrivial = 0; 776 if (acl_flavor & _ACL_ACLENT_ENABLED) { 777 if (vsecattr.vsa_aclcnt > MIN_ACL_ENTRIES) 778 isnontrivial = 1; 779 if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp != NULL) 780 kmem_free(vsecattr.vsa_aclentp, 781 vsecattr.vsa_aclcnt * sizeof (aclent_t)); 782 if (vsecattr.vsa_dfaclcnt && vsecattr.vsa_dfaclentp != NULL) 783 kmem_free(vsecattr.vsa_dfaclentp, 784 vsecattr.vsa_dfaclcnt * sizeof (aclent_t)); 785 } 786 if (acl_flavor & _ACL_ACE_ENABLED) { 787 788 isnontrivial = ace_trivial(vsecattr.vsa_aclentp, 789 vsecattr.vsa_aclcnt); 790 791 if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp != NULL) 792 kmem_free(vsecattr.vsa_aclentp, 793 vsecattr.vsa_aclcnt * sizeof (ace_t)); 794 /* ACE has no vsecattr.vsa_dfaclcnt */ 795 } 796 return (isnontrivial); 797 } 798 799 /* 800 * Check whether we need a retry to recover from STALE error. 801 */ 802 int 803 fs_need_estale_retry(int retry_count) 804 { 805 if (retry_count < fs_estale_retry) 806 return (1); 807 else 808 return (0); 809 } 810 811 812 static int (*fs_av_scan)(vnode_t *, cred_t *, int) = NULL; 813 814 /* 815 * Routine for anti-virus scanner to call to register its scanning routine. 816 */ 817 void 818 fs_vscan_register(int (*av_scan)(vnode_t *, cred_t *, int)) 819 { 820 fs_av_scan = av_scan; 821 } 822 823 /* 824 * Routine for file systems to call to initiate anti-virus scanning. 825 * Scanning will only be done on REGular files (currently). 826 */ 827 int 828 fs_vscan(vnode_t *vp, cred_t *cr, int async) 829 { 830 int ret = 0; 831 832 if (fs_av_scan && vp->v_type == VREG) 833 ret = (*fs_av_scan)(vp, cr, async); 834 835 return (ret); 836 } 837