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 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <sys/uio.h>
  29 #include <sys/statvfs.h>
  30 #include <sys/vnode.h>
  31 #include <sys/thread.h>
  32 #include <sys/pathname.h>
  33 #include <sys/cred.h>
  34 #include <sys/extdirent.h>
  35 #include <sys/nbmlock.h>
  36 #include <sys/share.h>
  37 #include <sys/fcntl.h>
  38 #include <nfs/lm.h>
  39 
  40 #include <smbsrv/smb_kproto.h>
  41 #include <smbsrv/string.h>
  42 #include <smbsrv/smb_vops.h>
  43 #include <smbsrv/smb_fsops.h>
  44 
  45 /*
  46  * CATIA support
  47  *
  48  * CATIA V4 is a UNIX product and uses characters in filenames that
  49  * are considered invalid by Windows. CATIA V5 is available on both
  50  * UNIX and Windows.  Thus, as CATIA customers migrate from V4 to V5,
  51  * some V4 files could become inaccessible to windows clients if the
  52  * filename contains the characters that are considered illegal in
  53  * Windows.  In order to address this issue an optional character
  54  * translation is applied to filenames at the smb_vop interface.
  55  *
  56  * Character Translation Table
  57  * ----------------------------------
  58  * Unix-char (v4) | Windows-char (v5)
  59  * ----------------------------------
  60  *        *       |  0x00a4  Currency Sign
  61  *        |       |  0x00a6  Broken Bar
  62  *        "       |  0x00a8  Diaeresis
  63  *        <       |  0x00ab  Left-Pointing Double Angle Quotation Mark
  64  *        >       |  0x00bb  Right-Pointing Double Angle Quotation Mark
  65  *        ?       |  0x00bf  Inverted Question mark
  66  *        :       |  0x00f7  Division Sign
  67  *        /       |  0x00f8  Latin Small Letter o with stroke
  68  *        \       |  0x00ff  Latin Small Letter Y with Diaeresis
  69  *
  70  *
  71  * Two lookup tables are used to perform the character translation:
  72  *
  73  * smb_catia_v5_lookup - provides the mapping between UNIX ASCII (v4)
  74  * characters and equivalent or translated wide characters.
  75  * It is indexed by the decimal value of the ASCII character (0-127).
  76  *
  77  * smb_catia_v4_lookup - provides the mapping between wide characters
  78  * in the range from 0x00A4 to 0x00FF and their UNIX (v4) equivalent
  79  * (in wide character format).  It is indexed by the decimal value of
  80  * the wide character (164-255) with an offset of -164.
  81  * If this translation produces a filename containing a '/' create, mkdir
  82  * or rename (to the '/' name)  operations will not be permitted. It is
  83  * not valid to create a filename with a '/' in it. However, if such a
  84  * file already exists other operations (e.g, lookup, delete, rename)
  85  * are permitted on it.
  86  */
  87 
  88 /* number of characters mapped */
  89 #define SMB_CATIA_NUM_MAPS              9
  90 
  91 /* Windows Characters used in special character mapping */
  92 #define SMB_CATIA_WIN_CURRENCY          0x00a4
  93 #define SMB_CATIA_WIN_BROKEN_BAR        0x00a6
  94 #define SMB_CATIA_WIN_DIAERESIS         0x00a8
  95 #define SMB_CATIA_WIN_LEFT_ANGLE        0x00ab
  96 #define SMB_CATIA_WIN_RIGHT_ANGLE       0x00bb
  97 #define SMB_CATIA_WIN_INVERTED_QUESTION 0x00bf
  98 #define SMB_CATIA_WIN_DIVISION          0x00f7
  99 #define SMB_CATIA_WIN_LATIN_O           0x00f8
 100 #define SMB_CATIA_WIN_LATIN_Y           0x00ff
 101 
 102 #define SMB_CATIA_V4_LOOKUP_LOW         SMB_CATIA_WIN_CURRENCY
 103 #define SMB_CATIA_V4_LOOKUP_UPPER       SMB_CATIA_WIN_LATIN_Y
 104 #define SMB_CATIA_V4_LOOKUP_MAX         \
 105         (SMB_CATIA_V4_LOOKUP_UPPER - SMB_CATIA_V4_LOOKUP_LOW + 1)
 106 #define SMB_CATIA_V5_LOOKUP_MAX         0x0080
 107 
 108 typedef struct smb_catia_map
 109 {
 110         unsigned char unixchar; /* v4 */
 111         smb_wchar_t winchar;    /* v5 */
 112 } smb_catia_map_t;
 113 
 114 smb_catia_map_t const catia_maps[SMB_CATIA_NUM_MAPS] =
 115 {
 116         {'"',  SMB_CATIA_WIN_DIAERESIS},
 117         {'*',  SMB_CATIA_WIN_CURRENCY},
 118         {':',  SMB_CATIA_WIN_DIVISION},
 119         {'<',  SMB_CATIA_WIN_LEFT_ANGLE},
 120         {'>',  SMB_CATIA_WIN_RIGHT_ANGLE},
 121         {'?',  SMB_CATIA_WIN_INVERTED_QUESTION},
 122         {'\\', SMB_CATIA_WIN_LATIN_Y},
 123         {'/',  SMB_CATIA_WIN_LATIN_O},
 124         {'|',  SMB_CATIA_WIN_BROKEN_BAR}
 125 };
 126 
 127 static smb_wchar_t smb_catia_v5_lookup[SMB_CATIA_V5_LOOKUP_MAX];
 128 static smb_wchar_t smb_catia_v4_lookup[SMB_CATIA_V4_LOOKUP_MAX];
 129 
 130 static void smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr);
 131 static void smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp);
 132 static callb_cpr_t *smb_lock_frlock_callback(flk_cb_when_t, void *);
 133 static void smb_vop_catia_init();
 134 
 135 extern sysid_t lm_alloc_sysidt();
 136 
 137 #define SMB_AT_MAX      16
 138 static const uint_t smb_attrmap[SMB_AT_MAX] = {
 139         0,
 140         AT_TYPE,
 141         AT_MODE,
 142         AT_UID,
 143         AT_GID,
 144         AT_FSID,
 145         AT_NODEID,
 146         AT_NLINK,
 147         AT_SIZE,
 148         AT_ATIME,
 149         AT_MTIME,
 150         AT_CTIME,
 151         AT_RDEV,
 152         AT_BLKSIZE,
 153         AT_NBLOCKS,
 154         AT_SEQ
 155 };
 156 
 157 static boolean_t        smb_vop_initialized = B_FALSE;
 158 caller_context_t        smb_ct;
 159 
 160 /*
 161  * smb_vop_init
 162  *
 163  * This function is not multi-thread safe. The caller must make sure only one
 164  * thread makes the call.
 165  */
 166 int
 167 smb_vop_init(void)
 168 {
 169         if (smb_vop_initialized)
 170                 return (0);
 171         /*
 172          * The caller_context will be used primarily for range locking.
 173          * Since the CIFS server is mapping its locks to POSIX locks,
 174          * only one pid is used for operations originating from the
 175          * CIFS server (to represent CIFS in the VOP_FRLOCK routines).
 176          *
 177          * XXX: Should smb_ct be per-zone?
 178          */
 179         smb_ct.cc_sysid = lm_alloc_sysidt();
 180         if (smb_ct.cc_sysid == LM_NOSYSID)
 181                 return (ENOMEM);
 182 
 183         smb_ct.cc_caller_id = fs_new_caller_id();
 184         smb_ct.cc_pid = IGN_PID;
 185         smb_ct.cc_flags = 0;
 186         smb_vop_catia_init();
 187 
 188         smb_vop_initialized = B_TRUE;
 189         return (0);
 190 }
 191 
 192 /*
 193  * smb_vop_fini
 194  *
 195  * This function is not multi-thread safe. The caller must make sure only one
 196  * thread makes the call.
 197  */
 198 void
 199 smb_vop_fini(void)
 200 {
 201         if (!smb_vop_initialized)
 202                 return;
 203 
 204         lm_free_sysidt(smb_ct.cc_sysid);
 205         smb_ct.cc_pid = IGN_PID;
 206         smb_ct.cc_sysid = LM_NOSYSID;
 207         smb_vop_initialized = B_FALSE;
 208 }
 209 
 210 /*
 211  * The smb_ct will be used primarily for range locking.
 212  * Since the CIFS server is mapping its locks to POSIX locks,
 213  * only one pid is used for operations originating from the
 214  * CIFS server (to represent CIFS in the VOP_FRLOCK routines).
 215  */
 216 int
 217 smb_vop_open(vnode_t **vpp, int mode, cred_t *cred)
 218 {
 219         return (VOP_OPEN(vpp, mode, cred, &smb_ct));
 220 }
 221 
 222 void
 223 smb_vop_close(vnode_t *vp, int mode, cred_t *cred)
 224 {
 225         (void) VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, &smb_ct);
 226 }
 227 
 228 int
 229 smb_vop_other_opens(vnode_t *vp, int mode)
 230 {
 231         return (((mode & FWRITE) && vn_has_other_opens(vp, V_WRITE)) ||
 232             (((mode & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) ||
 233             ((mode & FREAD) && vn_has_other_opens(vp, V_READ)) ||
 234             (((mode & FREAD) == 0) && vn_is_opened(vp, V_READ)) ||
 235             vn_is_mapped(vp, V_RDORWR));
 236 }
 237 
 238 /*
 239  * The smb_vop_* functions have minimal knowledge of CIFS semantics and
 240  * serve as an interface to the VFS layer.
 241  *
 242  * Only smb_fsop_* layer functions should call smb_vop_* layer functions.
 243  * (Higher-level CIFS service code should never skip the smb_fsop_* layer
 244  * to call smb_vop_* layer functions directly.)
 245  */
 246 
 247 /*
 248  * XXX - Extended attributes support in the file system assumed.
 249  * This is needed for full NT Streams functionality.
 250  */
 251 
 252 int
 253 smb_vop_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr)
 254 {
 255         int error;
 256 
 257         (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
 258         error = VOP_READ(vp, uiop, ioflag, cr, &smb_ct);
 259         VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
 260         return (error);
 261 }
 262 
 263 int
 264 smb_vop_write(vnode_t *vp, uio_t *uiop, int ioflag, uint32_t *lcount,
 265     cred_t *cr)
 266 {
 267         int error;
 268 
 269         *lcount = uiop->uio_resid;
 270 
 271         uiop->uio_llimit = MAXOFFSET_T;
 272 
 273         (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
 274         error = VOP_WRITE(vp, uiop, ioflag, cr, &smb_ct);
 275         VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
 276 
 277         *lcount -= uiop->uio_resid;
 278 
 279         return (error);
 280 }
 281 
 282 int
 283 smb_vop_ioctl(vnode_t *vp, int cmd, void *arg, cred_t *cr)
 284 {
 285         int error, rval = 0;
 286         uint_t flags = 0;
 287 
 288 #ifdef  FKIOCTL
 289         flags |= FKIOCTL;
 290 #endif
 291         error = VOP_IOCTL(vp, cmd, (intptr_t)arg, (int)flags, cr,
 292             &rval, &smb_ct);
 293         if (error != 0)
 294                 rval = error;
 295 
 296         return (rval);
 297 }
 298 
 299 /*
 300  * smb_vop_getattr()
 301  *
 302  * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS
 303  * service (instead of calling VOP_GETATTR directly) to retrieve attributes
 304  * due to special processing needed for streams files.
 305  *
 306  * All attributes are retrieved.
 307  *
 308  * When vp denotes a named stream, then unnamed_vp should be passed in (denoting
 309  * the corresponding unnamed stream).
 310  * A named stream's attributes (as far as CIFS is concerned) are those of the
 311  * unnamed stream (minus the size attribute, and the type), plus  the size of
 312  * the named stream, and a type value of VREG.
 313  * Although the file system may store other attributes with the named stream,
 314  * these should not be used by CIFS for any purpose.
 315  *
 316  * File systems without VFSFT_XVATTR do not support DOS attributes or create
 317  * time (crtime). In this case the mtime is used as the crtime.
 318  * Likewise if VOP_GETATTR doesn't return any system attributes the dosattr
 319  * is 0 and the mtime is used as the crtime.
 320  */
 321 int
 322 smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr,
 323     int flags, cred_t *cr)
 324 {
 325         int error;
 326         vnode_t *use_vp;
 327         smb_attr_t tmp_attr;
 328         xvattr_t tmp_xvattr;
 329         xoptattr_t *xoap = NULL;
 330 
 331         if (unnamed_vp)
 332                 use_vp = unnamed_vp;
 333         else
 334                 use_vp = vp;
 335 
 336         if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
 337                 xva_init(&tmp_xvattr);
 338                 xoap = xva_getxoptattr(&tmp_xvattr);
 339                 ASSERT(xoap);
 340 
 341                 smb_sa_to_va_mask(ret_attr->sa_mask,
 342                     &tmp_xvattr.xva_vattr.va_mask);
 343 
 344                 XVA_SET_REQ(&tmp_xvattr, XAT_READONLY);
 345                 XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN);
 346                 XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM);
 347                 XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE);
 348                 XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME);
 349                 XVA_SET_REQ(&tmp_xvattr, XAT_REPARSE);
 350                 XVA_SET_REQ(&tmp_xvattr, XAT_OFFLINE);
 351                 XVA_SET_REQ(&tmp_xvattr, XAT_SPARSE);
 352 
 353                 error = VOP_GETATTR(use_vp, &tmp_xvattr.xva_vattr, flags,
 354                     cr, &smb_ct);
 355                 if (error != 0)
 356                         return (error);
 357 
 358                 ret_attr->sa_vattr = tmp_xvattr.xva_vattr;
 359                 ret_attr->sa_dosattr = 0;
 360 
 361                 if (tmp_xvattr.xva_vattr.va_mask & AT_XVATTR) {
 362                         xoap = xva_getxoptattr(&tmp_xvattr);
 363                         ASSERT(xoap);
 364 
 365                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) &&
 366                             (xoap->xoa_readonly)) {
 367                                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
 368                         }
 369 
 370                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) &&
 371                             (xoap->xoa_hidden)) {
 372                                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN;
 373                         }
 374 
 375                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) &&
 376                             (xoap->xoa_system)) {
 377                                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM;
 378                         }
 379 
 380                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) &&
 381                             (xoap->xoa_archive)) {
 382                                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE;
 383                         }
 384 
 385                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_REPARSE)) &&
 386                             (xoap->xoa_reparse)) {
 387                                 ret_attr->sa_dosattr |=
 388                                     FILE_ATTRIBUTE_REPARSE_POINT;
 389                         }
 390 
 391                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_OFFLINE)) &&
 392                             (xoap->xoa_offline)) {
 393                                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_OFFLINE;
 394                         }
 395 
 396                         if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SPARSE)) &&
 397                             (xoap->xoa_sparse)) {
 398                                 ret_attr->sa_dosattr |=
 399                                     FILE_ATTRIBUTE_SPARSE_FILE;
 400                         }
 401 
 402                         ret_attr->sa_crtime = xoap->xoa_createtime;
 403                 } else {
 404                         ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime;
 405                 }
 406         } else {
 407                 /*
 408                  * Support for file systems without VFSFT_XVATTR
 409                  */
 410                 smb_sa_to_va_mask(ret_attr->sa_mask,
 411                     &ret_attr->sa_vattr.va_mask);
 412 
 413                 error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr,
 414                     flags, cr, &smb_ct);
 415                 if (error != 0)
 416                         return (error);
 417 
 418                 ret_attr->sa_dosattr = 0;
 419                 ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime;
 420         }
 421 
 422         if (unnamed_vp) {
 423                 ret_attr->sa_vattr.va_type = VREG;
 424 
 425                 if (ret_attr->sa_mask & (SMB_AT_SIZE | SMB_AT_NBLOCKS)) {
 426                         tmp_attr.sa_vattr.va_mask = AT_SIZE | AT_NBLOCKS;
 427 
 428                         error = VOP_GETATTR(vp, &tmp_attr.sa_vattr,
 429                             flags, cr, &smb_ct);
 430                         if (error != 0)
 431                                 return (error);
 432 
 433                         ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size;
 434                         ret_attr->sa_vattr.va_nblocks =
 435                             tmp_attr.sa_vattr.va_nblocks;
 436                 }
 437         }
 438 
 439         if (ret_attr->sa_vattr.va_type == VDIR)
 440                 ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY;
 441 
 442         return (error);
 443 }
 444 
 445 /*
 446  * smb_vop_setattr()
 447  *
 448  * smb_fsop_setattr()/smb_vop_setattr() should always be used instead of
 449  * VOP_SETATTR() when calling from the CIFS service, due to special processing
 450  * for streams files.
 451  *
 452  * Streams have a size but otherwise do not have separate attributes from
 453  * the (unnamed stream) file, i.e., the security and ownership of the file
 454  * applies to the stream.  In contrast, extended attribute files, which are
 455  * used to implement streams, are independent objects with their own
 456  * attributes.
 457  *
 458  * For compatibility with streams, we set the size on the extended attribute
 459  * file and apply other attributes to the (unnamed stream) file.  The one
 460  * exception is that the UID and GID can be set on the stream by passing a
 461  * NULL unnamed_vp, which allows callers to synchronize stream ownership
 462  * with the (unnamed stream) file.
 463  */
 464 int
 465 smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *attr,
 466     int flags, cred_t *cr)
 467 {
 468         int error = 0;
 469         int at_size = 0;
 470         vnode_t *use_vp;
 471         xvattr_t xvattr;
 472         vattr_t *vap;
 473 
 474         if (attr->sa_mask & SMB_AT_DOSATTR) {
 475                 attr->sa_dosattr &=
 476                     (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY |
 477                     FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
 478                     FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_SPARSE_FILE);
 479         }
 480 
 481         if (unnamed_vp) {
 482                 use_vp = unnamed_vp;
 483                 if (attr->sa_mask & SMB_AT_SIZE) {
 484                         at_size = 1;
 485                         attr->sa_mask &= ~SMB_AT_SIZE;
 486                 }
 487         } else {
 488                 use_vp = vp;
 489         }
 490 
 491         /*
 492          * The caller should not be setting sa_vattr.va_mask,
 493          * but rather sa_mask.
 494          */
 495 
 496         attr->sa_vattr.va_mask = 0;
 497 
 498         if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
 499                 smb_vop_setup_xvattr(attr, &xvattr);
 500                 vap = &xvattr.xva_vattr;
 501         } else {
 502                 smb_sa_to_va_mask(attr->sa_mask,
 503                     &attr->sa_vattr.va_mask);
 504                 vap = &attr->sa_vattr;
 505         }
 506 
 507         if ((error = VOP_SETATTR(use_vp, vap, flags, cr, &smb_ct)) != 0)
 508                 return (error);
 509 
 510         if (at_size) {
 511                 attr->sa_vattr.va_mask = AT_SIZE;
 512                 error = VOP_SETATTR(vp, &attr->sa_vattr, flags,
 513                     zone_kcred(), &smb_ct);
 514         }
 515 
 516         return (error);
 517 }
 518 
 519 int
 520 smb_vop_space(vnode_t *vp, int cmd, flock64_t *bfp, int flags,
 521     offset_t offset, cred_t *cr)
 522 {
 523         int error;
 524 
 525         error = VOP_SPACE(vp, cmd, bfp, flags, offset, cr, &smb_ct);
 526 
 527         return (error);
 528 }
 529 
 530 /*
 531  * smb_vop_access
 532  *
 533  * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode
 534  * against file's ACL or Unix permissions. CIFS on the other hand needs to
 535  * know if the requested operation can succeed for the given object, this
 536  * requires more checks in case of DELETE bit since permissions on the parent
 537  * directory are important as well. Based on Windows rules if parent's ACL
 538  * grant FILE_DELETE_CHILD a file can be delete regardless of the file's
 539  * permissions.
 540  */
 541 int
 542 smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr)
 543 {
 544         int error = 0;
 545 
 546         if (mode == 0)
 547                 return (0);
 548 
 549         error = VOP_ACCESS(vp, mode, flags, cr, NULL);
 550 
 551         if (error == 0)
 552                 return (0);
 553 
 554         if ((mode & (ACE_DELETE|ACE_READ_ATTRIBUTES)) == 0 ||
 555             flags != V_ACE_MASK || dir_vp == NULL)
 556                 return (error);
 557 
 558         smb_audit_save();
 559         if ((mode & ACE_DELETE) != 0) {
 560                 error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags,
 561                     cr, NULL);
 562 
 563                 if (error == 0)
 564                         mode &= ~ACE_DELETE;
 565         }
 566         if ((mode & ACE_READ_ATTRIBUTES) != 0) {
 567                 error = VOP_ACCESS(dir_vp, ACE_LIST_DIRECTORY, flags,
 568                     cr, NULL);
 569 
 570                 if (error == 0)
 571                         mode &= ~ACE_READ_ATTRIBUTES;
 572         }
 573 
 574         if (mode != 0)
 575                 error = VOP_ACCESS(vp, mode, flags, cr, NULL);
 576 
 577         smb_audit_load();
 578         return (error);
 579 }
 580 
 581 /*
 582  * smb_vop_lookup
 583  *
 584  * dvp:         directory vnode (in)
 585  * name:        name of file to be looked up (in)
 586  * vpp:         looked-up vnode (out)
 587  * od_name:     on-disk name of file (out).
 588  *              This parameter is optional.  If a pointer is passed in, it
 589  *              must be allocated with MAXNAMELEN bytes
 590  * rootvp:      vnode of the tree root (in)
 591  *              This parameter is always passed in non-NULL except at the time
 592  *              of share set up.
 593  * direntflags: dirent flags returned from VOP_LOOKUP
 594  */
 595 int
 596 smb_vop_lookup(
 597     vnode_t             *dvp,
 598     char                *name,
 599     vnode_t             **vpp,
 600     char                *od_name,
 601     int                 flags,
 602     int                 *direntflags,
 603     vnode_t             *rootvp,
 604     smb_attr_t          *attr,
 605     cred_t              *cr)
 606 {
 607         int error = 0;
 608         int option_flags = 0;
 609         pathname_t rpn;
 610         char *np = name;
 611         char namebuf[MAXNAMELEN];
 612 
 613         if (*name == '\0')
 614                 return (EINVAL);
 615 
 616         ASSERT(vpp);
 617         *vpp = NULL;
 618         *direntflags = 0;
 619 
 620         if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) {
 621                 if (rootvp && (dvp == rootvp)) {
 622                         VN_HOLD(dvp);
 623                         *vpp = dvp;
 624                         return (0);
 625                 }
 626 
 627                 if (dvp->v_flag & VROOT) {
 628                         vfs_t *vfsp;
 629                         vnode_t *cvp = dvp;
 630 
 631                         /*
 632                          * Set dvp and check for races with forced unmount
 633                          * (see lookuppnvp())
 634                          */
 635 
 636                         vfsp = cvp->v_vfsp;
 637                         vfs_rlock_wait(vfsp);
 638                         if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
 639                             (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
 640                                 vfs_unlock(vfsp);
 641                                 return (EIO);
 642                         }
 643                         vfs_unlock(vfsp);
 644                 }
 645         }
 646 
 647         if (flags & SMB_IGNORE_CASE)
 648                 option_flags = FIGNORECASE;
 649 
 650         if (flags & SMB_CATIA)
 651                 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf));
 652 
 653         pn_alloc(&rpn);
 654 
 655         /*
 656          * Easier to not have junk in rpn, as not every FS type
 657          * will necessarily fill that in for us.
 658          */
 659         bzero(rpn.pn_buf, rpn.pn_bufsize);
 660 
 661         error = VOP_LOOKUP(dvp, np, vpp, NULL, option_flags, NULL, cr,
 662             &smb_ct, direntflags, &rpn);
 663 
 664         if (error == 0) {
 665                 if (od_name) {
 666                         bzero(od_name, MAXNAMELEN);
 667                         if ((option_flags & FIGNORECASE) != 0 &&
 668                             rpn.pn_buf[0] != '\0')
 669                                 np = rpn.pn_buf;
 670                         else
 671                                 np = name;
 672                         if (flags & SMB_CATIA)
 673                                 smb_vop_catia_v4tov5(np, od_name, MAXNAMELEN);
 674                         else
 675                                 (void) strlcpy(od_name, np, MAXNAMELEN);
 676                 }
 677 
 678                 if (attr != NULL) {
 679                         attr->sa_mask = SMB_AT_ALL;
 680                         (void) smb_vop_getattr(*vpp, NULL, attr, 0,
 681                             zone_kcred());
 682                 }
 683         }
 684 
 685         pn_free(&rpn);
 686         return (error);
 687 }
 688 
 689 int
 690 smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
 691     int flags, cred_t *cr, vsecattr_t *vsap)
 692 {
 693         int error;
 694         int option_flags = 0;
 695         xvattr_t xvattr;
 696         vattr_t *vap;
 697         char *np = name;
 698         char namebuf[MAXNAMELEN];
 699 
 700         if (flags & SMB_IGNORE_CASE)
 701                 option_flags = FIGNORECASE;
 702 
 703         attr->sa_vattr.va_mask = 0;
 704 
 705         if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) {
 706                 smb_vop_setup_xvattr(attr, &xvattr);
 707                 vap = &xvattr.xva_vattr;
 708         } else {
 709                 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
 710                 vap = &attr->sa_vattr;
 711         }
 712 
 713         if (flags & SMB_CATIA) {
 714                 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf));
 715                 if (strchr(np, '/') != NULL)
 716                         return (EILSEQ);
 717         }
 718 
 719         error = VOP_CREATE(dvp, np, vap, EXCL, attr->sa_vattr.va_mode,
 720             vpp, cr, option_flags, &smb_ct, vsap);
 721 
 722         /*
 723          * One could argue that filesystems should obey the size
 724          * if specified in the create attributes.  Unfortunately,
 725          * they only appear to let you truncate the size to zero.
 726          * SMB needs to set a non-zero size, so work-around.
 727          */
 728         if (error == 0 && *vpp != NULL &&
 729             (vap->va_mask & AT_SIZE) != 0 &&
 730             vap->va_size > 0) {
 731                 vattr_t ta = *vap;
 732                 ta.va_mask = AT_SIZE;
 733                 (void) VOP_SETATTR(*vpp, &ta, 0, cr, &smb_ct);
 734         }
 735 
 736         return (error);
 737 }
 738 
 739 int
 740 smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr)
 741 {
 742         int error;
 743         int option_flags = 0;
 744         char *np = name;
 745         char namebuf[MAXNAMELEN];
 746 
 747         if (flags & SMB_IGNORE_CASE)
 748                 option_flags = FIGNORECASE;
 749 
 750         if (flags & SMB_CATIA)
 751                 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf));
 752 
 753         error = VOP_REMOVE(dvp, np, cr, &smb_ct, option_flags);
 754 
 755         return (error);
 756 }
 757 
 758 /*
 759  * smb_vop_link(target-dir-vp, source-file-vp, target-name)
 760  *
 761  * Create a link - same tree (identical TID) only.
 762  */
 763 int
 764 smb_vop_link(vnode_t *to_dvp, vnode_t *from_vp, char *to_name,
 765     int flags, cred_t *cr)
 766 {
 767         int option_flags = 0;
 768         char *np, *buf;
 769         int rc;
 770 
 771         if (flags & SMB_IGNORE_CASE)
 772                 option_flags = FIGNORECASE;
 773 
 774         if (flags & SMB_CATIA) {
 775                 buf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
 776                 np = smb_vop_catia_v5tov4(to_name, buf, MAXNAMELEN);
 777                 if (strchr(np, '/') != NULL) {
 778                         kmem_free(buf, MAXNAMELEN);
 779                         return (EILSEQ);
 780                 }
 781 
 782                 rc = VOP_LINK(to_dvp, from_vp, np, cr, &smb_ct, option_flags);
 783                 kmem_free(buf, MAXNAMELEN);
 784                 return (rc);
 785         }
 786 
 787         rc = VOP_LINK(to_dvp, from_vp, to_name, cr, &smb_ct, option_flags);
 788         return (rc);
 789 }
 790 
 791 /*
 792  * smb_vop_rename()
 793  *
 794  * The rename is for files in the same tree (identical TID) only.
 795  */
 796 int
 797 smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp,
 798     char *to_name, int flags, cred_t *cr)
 799 {
 800         int error;
 801         int option_flags = 0;
 802         char *from, *to, *fbuf, *tbuf;
 803 
 804         if (flags & SMB_IGNORE_CASE)
 805                 option_flags = FIGNORECASE;
 806 
 807         if (flags & SMB_CATIA) {
 808                 tbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
 809                 to = smb_vop_catia_v5tov4(to_name, tbuf, MAXNAMELEN);
 810                 if (strchr(to, '/') != NULL) {
 811                         kmem_free(tbuf, MAXNAMELEN);
 812                         return (EILSEQ);
 813                 }
 814 
 815                 fbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
 816                 from = smb_vop_catia_v5tov4(from_name, fbuf, MAXNAMELEN);
 817 
 818                 error = VOP_RENAME(from_dvp, from, to_dvp, to, cr,
 819                     &smb_ct, option_flags);
 820 
 821                 kmem_free(tbuf, MAXNAMELEN);
 822                 kmem_free(fbuf, MAXNAMELEN);
 823                 return (error);
 824         }
 825 
 826         error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr,
 827             &smb_ct, option_flags);
 828 
 829         return (error);
 830 }
 831 
 832 int
 833 smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
 834     int flags, cred_t *cr, vsecattr_t *vsap)
 835 {
 836         int error;
 837         int option_flags = 0;
 838         xvattr_t xvattr;
 839         vattr_t *vap;
 840         char *np = name;
 841         char namebuf[MAXNAMELEN];
 842 
 843         if (flags & SMB_IGNORE_CASE)
 844                 option_flags = FIGNORECASE;
 845 
 846         attr->sa_vattr.va_mask = 0;
 847 
 848         if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) {
 849                 smb_vop_setup_xvattr(attr, &xvattr);
 850                 vap = &xvattr.xva_vattr;
 851         } else {
 852                 smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
 853                 vap = &attr->sa_vattr;
 854         }
 855 
 856         if (flags & SMB_CATIA) {
 857                 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf));
 858                 if (strchr(np, '/') != NULL)
 859                         return (EILSEQ);
 860         }
 861 
 862         error = VOP_MKDIR(dvp, np, vap, vpp, cr, &smb_ct, option_flags, vsap);
 863 
 864         return (error);
 865 }
 866 
 867 /*
 868  * smb_vop_rmdir()
 869  *
 870  * Only simple rmdir supported, consistent with NT semantics
 871  * (can only remove an empty directory).
 872  *
 873  * The third argument to VOP_RMDIR  is the current directory of
 874  * the process.  It allows rmdir wants to EINVAL if one tries to
 875  * remove ".".  Since SMB servers do not know what their clients'
 876  * current directories are, we fake it by supplying a vnode known
 877  * to exist and illegal to remove (rootdir).
 878  */
 879 int
 880 smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr)
 881 {
 882         int error;
 883         int option_flags = 0;
 884         char *np = name;
 885         char namebuf[MAXNAMELEN];
 886 
 887         if (flags & SMB_IGNORE_CASE)
 888                 option_flags = FIGNORECASE;
 889 
 890         if (flags & SMB_CATIA)
 891                 np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf));
 892 
 893         error = VOP_RMDIR(dvp, np, rootdir, cr, &smb_ct, option_flags);
 894         return (error);
 895 }
 896 
 897 int
 898 smb_vop_commit(vnode_t *vp, cred_t *cr)
 899 {
 900         return (VOP_FSYNC(vp, 1, cr, &smb_ct));
 901 }
 902 
 903 /*
 904  * Some code in smb_node.c needs to know which DOS attributes
 905  * we can actually store.  Let's define a mask here of all the
 906  * DOS attribute flags supported by the following function.
 907  */
 908 const uint32_t
 909 smb_vop_dosattr_settable =
 910         FILE_ATTRIBUTE_ARCHIVE |
 911         FILE_ATTRIBUTE_SYSTEM |
 912         FILE_ATTRIBUTE_HIDDEN |
 913         FILE_ATTRIBUTE_READONLY |
 914         FILE_ATTRIBUTE_OFFLINE |
 915         FILE_ATTRIBUTE_SPARSE_FILE;
 916 
 917 static void
 918 smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr)
 919 {
 920         xoptattr_t *xoap = NULL;
 921         uint_t xva_mask;
 922 
 923         /*
 924          * Initialize xvattr, including bzero
 925          */
 926         xva_init(xvattr);
 927         xoap = xva_getxoptattr(xvattr);
 928 
 929         ASSERT(xoap);
 930 
 931         /*
 932          * Copy caller-specified classic attributes to xvattr.
 933          * First save xvattr's mask (set in xva_init()), which
 934          * contains AT_XVATTR.  This is |'d in later if needed.
 935          */
 936 
 937         xva_mask = xvattr->xva_vattr.va_mask;
 938         xvattr->xva_vattr = smb_attr->sa_vattr;
 939 
 940         smb_sa_to_va_mask(smb_attr->sa_mask, &xvattr->xva_vattr.va_mask);
 941 
 942         /*
 943          * Do not set ctime (only the file system can do it)
 944          */
 945 
 946         xvattr->xva_vattr.va_mask &= ~AT_CTIME;
 947 
 948         if (smb_attr->sa_mask & SMB_AT_DOSATTR) {
 949 
 950                 /*
 951                  * "|" in the original xva_mask, which contains
 952                  * AT_XVATTR
 953                  */
 954 
 955                 xvattr->xva_vattr.va_mask |= xva_mask;
 956 
 957                 XVA_SET_REQ(xvattr, XAT_ARCHIVE);
 958                 XVA_SET_REQ(xvattr, XAT_SYSTEM);
 959                 XVA_SET_REQ(xvattr, XAT_READONLY);
 960                 XVA_SET_REQ(xvattr, XAT_HIDDEN);
 961                 XVA_SET_REQ(xvattr, XAT_OFFLINE);
 962                 XVA_SET_REQ(xvattr, XAT_SPARSE);
 963 
 964                 /*
 965                  * smb_attr->sa_dosattr: If a given bit is not set,
 966                  * that indicates that the corresponding field needs
 967                  * to be updated with a "0" value.  This is done
 968                  * implicitly as the xoap->xoa_* fields were bzero'd.
 969                  */
 970 
 971                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE)
 972                         xoap->xoa_archive = 1;
 973 
 974                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM)
 975                         xoap->xoa_system = 1;
 976 
 977                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY)
 978                         xoap->xoa_readonly = 1;
 979 
 980                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN)
 981                         xoap->xoa_hidden = 1;
 982 
 983                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_OFFLINE)
 984                         xoap->xoa_offline = 1;
 985 
 986                 if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)
 987                         xoap->xoa_sparse = 1;
 988         }
 989 
 990         if (smb_attr->sa_mask & SMB_AT_CRTIME) {
 991                 /*
 992                  * "|" in the original xva_mask, which contains
 993                  * AT_XVATTR
 994                  */
 995 
 996                 xvattr->xva_vattr.va_mask |= xva_mask;
 997                 XVA_SET_REQ(xvattr, XAT_CREATETIME);
 998                 xoap->xoa_createtime = smb_attr->sa_crtime;
 999         }
1000 }
1001 
1002 /*
1003  * smb_vop_readdir()
1004  *
1005  * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries.
1006  * The directory entries are returned in an fs-independent format by the
1007  * underlying file system.  That is, the "page" of information returned is
1008  * not literally stored on-disk in the format returned.
1009  * If the file system supports extended directory entries (has features
1010  * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be
1011  * filled with edirent_t structures, instead of dirent64_t structures.
1012  * If the file system supports access based enumeration (abe), set
1013  * V_RDDIR_ACCFILTER to filter directory entries based on user cred.
1014  */
1015 int
1016 smb_vop_readdir(vnode_t *vp, uint32_t offset,
1017     void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr)
1018 {
1019         int error = 0;
1020         int flags = 0;
1021         int rdirent_size;
1022         struct uio auio;
1023         struct iovec aiov;
1024 
1025         if (vp->v_type != VDIR)
1026                 return (ENOTDIR);
1027 
1028         if ((rddir_flag & SMB_EDIRENT) != 0 &&
1029             vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) {
1030                 flags |= V_RDDIR_ENTFLAGS;
1031                 rdirent_size = sizeof (edirent_t);
1032         } else {
1033                 rdirent_size = sizeof (dirent64_t);
1034         }
1035 
1036         if (*count < rdirent_size)
1037                 return (EINVAL);
1038 
1039         if (rddir_flag & SMB_ABE)
1040                 flags |= V_RDDIR_ACCFILTER;
1041 
1042         aiov.iov_base = buf;
1043         aiov.iov_len = *count;
1044         auio.uio_iov = &aiov;
1045         auio.uio_iovcnt = 1;
1046         auio.uio_loffset = (uint64_t)offset;
1047         auio.uio_segflg = UIO_SYSSPACE;
1048         auio.uio_extflg = UIO_COPY_DEFAULT;
1049         auio.uio_resid = *count;
1050         auio.uio_fmode = 0;
1051 
1052         (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
1053         error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags);
1054         VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
1055 
1056         if (error == 0)
1057                 *count = *count - auio.uio_resid;
1058 
1059         return (error);
1060 }
1061 
1062 /*
1063  * smb_sa_to_va_mask
1064  *
1065  * Set va_mask by running through the SMB_AT_* #define's and
1066  * setting those bits that correspond to the SMB_AT_* bits
1067  * set in sa_mask.
1068  */
1069 void
1070 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp)
1071 {
1072         int i;
1073         uint_t smask;
1074 
1075         smask = (sa_mask);
1076         for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) {
1077                 if (smask & 1)
1078                         *(va_maskp) |= smb_attrmap[i];
1079 
1080                 smask >>= 1;
1081         }
1082 }
1083 
1084 /*
1085  * smb_vop_stream_lookup()
1086  *
1087  * The name returned in od_name is the on-disk name of the stream with the
1088  * SMB_STREAM_PREFIX stripped off.  od_name should be allocated to MAXNAMELEN
1089  * by the caller.
1090  */
1091 int
1092 smb_vop_stream_lookup(
1093     vnode_t             *fvp,
1094     char                *stream_name,
1095     vnode_t             **vpp,
1096     char                *od_name,
1097     vnode_t             **xattrdirvpp,
1098     int                 flags,
1099     vnode_t             *rootvp,
1100     cred_t              *cr)
1101 {
1102         char *solaris_stream_name;
1103         char *name;
1104         int error, tmpflgs;
1105 
1106         if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
1107             LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0)
1108                 return (error);
1109 
1110         /*
1111          * Prepend SMB_STREAM_PREFIX to stream name
1112          */
1113 
1114         solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1115         (void) snprintf(solaris_stream_name, MAXNAMELEN,
1116             "%s%s", SMB_STREAM_PREFIX, stream_name);
1117 
1118         /*
1119          * "name" will hold the on-disk name returned from smb_vop_lookup
1120          * for the stream, including the SMB_STREAM_PREFIX.
1121          */
1122 
1123         name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
1124 
1125         if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp,
1126             name, flags, &tmpflgs, rootvp, NULL, cr)) != 0) {
1127                 VN_RELE(*xattrdirvpp);
1128         } else {
1129                 (void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]),
1130                     MAXNAMELEN);
1131         }
1132 
1133         kmem_free(solaris_stream_name, MAXNAMELEN);
1134         kmem_free(name, MAXNAMELEN);
1135 
1136         return (error);
1137 }
1138 
1139 int
1140 smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr,
1141     vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr)
1142 {
1143         char *solaris_stream_name;
1144         int error;
1145 
1146         if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
1147             LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0)
1148                 return (error);
1149 
1150         /*
1151          * Prepend SMB_STREAM_PREFIX to stream name
1152          */
1153 
1154         solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1155         (void) snprintf(solaris_stream_name, MAXNAMELEN,
1156             "%s%s", SMB_STREAM_PREFIX, stream_name);
1157 
1158         if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr,
1159             vpp, flags, cr, NULL)) != 0)
1160                 VN_RELE(*xattrdirvpp);
1161 
1162         kmem_free(solaris_stream_name, MAXNAMELEN);
1163 
1164         return (error);
1165 }
1166 
1167 int
1168 smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr)
1169 {
1170         char *solaris_stream_name;
1171         vnode_t *xattrdirvp;
1172         int error;
1173 
1174         error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr);
1175         if (error != 0)
1176                 return (error);
1177 
1178         /*
1179          * Prepend SMB_STREAM_PREFIX to stream name
1180          */
1181 
1182         solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1183         (void) snprintf(solaris_stream_name, MAXNAMELEN,
1184             "%s%s", SMB_STREAM_PREFIX, stream_name);
1185 
1186         /* XXX might have to use kcred */
1187         error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr);
1188 
1189         kmem_free(solaris_stream_name, MAXNAMELEN);
1190         VN_RELE(xattrdirvp);
1191 
1192         return (error);
1193 }
1194 
1195 int
1196 smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags,
1197     cred_t *cr)
1198 {
1199         int error;
1200 
1201         error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr,
1202             &smb_ct, NULL, NULL);
1203         return (error);
1204 }
1205 
1206 /*
1207  * smb_vop_traverse_check()
1208  *
1209  * This function checks to see if the passed-in vnode has a file system
1210  * mounted on it.  If it does, the mount point is "traversed" and the
1211  * vnode for the root of the file system is returned.
1212  */
1213 int
1214 smb_vop_traverse_check(vnode_t **vpp)
1215 {
1216         int error;
1217 
1218         if (vn_mountedvfs(*vpp) == 0)
1219                 return (0);
1220 
1221         /*
1222          * traverse() may return a different held vnode, even in the error case.
1223          * If it returns a different vnode, it will have released the original.
1224          */
1225 
1226         error = traverse(vpp);
1227 
1228         return (error);
1229 }
1230 
1231 int /*ARGSUSED*/
1232 smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr)
1233 {
1234         int error;
1235 
1236         error = VFS_STATVFS(vp->v_vfsp, statp);
1237 
1238         return (error);
1239 }
1240 
1241 /*
1242  * smb_vop_acl_read
1243  *
1244  * Reads the ACL of the specified file into 'aclp'.
1245  * acl_type is the type of ACL which the filesystem supports.
1246  *
1247  * Caller has to free the allocated memory for aclp by calling
1248  * acl_free().
1249  */
1250 int
1251 smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type,
1252     cred_t *cr)
1253 {
1254         int error;
1255         vsecattr_t vsecattr;
1256 
1257         ASSERT(vp);
1258         ASSERT(aclp);
1259 
1260         *aclp = NULL;
1261         bzero(&vsecattr, sizeof (vsecattr_t));
1262 
1263         switch (acl_type) {
1264         case ACLENT_T:
1265                 vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL |
1266                     VSA_DFACLCNT;
1267                 break;
1268 
1269         case ACE_T:
1270                 vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS;
1271                 break;
1272 
1273         default:
1274                 return (EINVAL);
1275         }
1276 
1277         if ((error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, &smb_ct)) != 0)
1278                 return (error);
1279 
1280         *aclp = smb_fsacl_from_vsa(&vsecattr, acl_type);
1281         if (vp->v_type == VDIR)
1282                 (*aclp)->acl_flags |= ACL_IS_DIR;
1283 
1284         return (0);
1285 }
1286 
1287 /*
1288  * smb_vop_acl_write
1289  *
1290  * Writes the given ACL in aclp for the specified file.
1291  */
1292 int
1293 smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr)
1294 {
1295         int error;
1296         vsecattr_t vsecattr;
1297         int aclbsize;
1298 
1299         ASSERT(vp);
1300         ASSERT(aclp);
1301 
1302         error = smb_fsacl_to_vsa(aclp, &vsecattr, &aclbsize);
1303 
1304         if (error == 0) {
1305                 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
1306                 error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, &smb_ct);
1307                 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
1308         }
1309 
1310         if (aclbsize && vsecattr.vsa_aclentp)
1311                 kmem_free(vsecattr.vsa_aclentp, aclbsize);
1312 
1313         return (error);
1314 }
1315 
1316 /*
1317  * smb_vop_acl_type
1318  *
1319  * Determines the ACL type for the given vnode.
1320  * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL.
1321  */
1322 acl_type_t
1323 smb_vop_acl_type(vnode_t *vp)
1324 {
1325         int error;
1326         ulong_t whichacl;
1327 
1328         error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl,
1329             zone_kcred(), NULL);
1330         if (error != 0) {
1331                 /*
1332                  * If we got an error, then the filesystem
1333                  * likely does not understand the _PC_ACL_ENABLED
1334                  * pathconf.  In this case, we fall back to trying
1335                  * POSIX-draft (aka UFS-style) ACLs.
1336                  */
1337                 whichacl = _ACL_ACLENT_ENABLED;
1338         }
1339 
1340         if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) {
1341                 /*
1342                  * If the file system supports neither ACE nor
1343                  * ACLENT ACLs we will fall back to UFS-style ACLs
1344                  * like we did above if there was an error upon
1345                  * calling VOP_PATHCONF.
1346                  *
1347                  * ACE and ACLENT type ACLs are the only interfaces
1348                  * supported thus far.  If any other bits are set on
1349                  * 'whichacl' upon return from VOP_PATHCONF, we will
1350                  * ignore them.
1351                  */
1352                 whichacl = _ACL_ACLENT_ENABLED;
1353         }
1354 
1355         if (whichacl == _ACL_ACLENT_ENABLED)
1356                 return (ACLENT_T);
1357 
1358         return (ACE_T);
1359 }
1360 
1361 static const int zfs_perms[] = {
1362         ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS,
1363         ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD,
1364         ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL,
1365         ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE
1366 };
1367 
1368 static const int unix_perms[] = { VREAD, VWRITE, VEXEC };
1369 /*
1370  * smb_vop_eaccess
1371  *
1372  * Returns the effective permission of the given credential for the
1373  * specified object.
1374  *
1375  * This is just a workaround. We need VFS/FS support for this.
1376  */
1377 void
1378 smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr)
1379 {
1380         int error, i;
1381         int pnum;
1382 
1383         *mode = 0;
1384 
1385         if (flags == V_ACE_MASK) {
1386                 pnum = sizeof (zfs_perms) / sizeof (int);
1387 
1388                 for (i = 0; i < pnum; i++) {
1389                         error = smb_vop_access(vp, zfs_perms[i], flags,
1390                             dir_vp, cr);
1391                         if (error == 0)
1392                                 *mode |= zfs_perms[i];
1393                 }
1394         } else {
1395                 pnum = sizeof (unix_perms) / sizeof (int);
1396 
1397                 for (i = 0; i < pnum; i++) {
1398                         error = smb_vop_access(vp, unix_perms[i], flags,
1399                             dir_vp, cr);
1400                         if (error == 0)
1401                                 *mode |= unix_perms[i];
1402                 }
1403         }
1404 }
1405 
1406 /*
1407  * See comments for smb_fsop_shrlock()
1408  */
1409 int
1410 smb_vop_shrlock(vnode_t *vp, uint32_t uniq_fid, uint32_t desired_access,
1411     uint32_t share_access, cred_t *cr)
1412 {
1413         struct shrlock shr;
1414         struct shr_locowner shr_own;
1415         short new_access = 0;
1416         short deny = 0;
1417         int flag = 0;
1418         int cmd;
1419 
1420         /*
1421          * share locking is not supported for non-regular
1422          * objects in NBMAND mode.
1423          */
1424         if (nbl_need_check(vp)) {
1425                 if (vp->v_type != VREG)
1426                         return (0);
1427 
1428                 cmd = F_SHARE_NBMAND;
1429         } else {
1430                 cmd = F_SHARE;
1431         }
1432 
1433         if ((desired_access & FILE_DATA_ALL) == 0) {
1434                 /* metadata access only */
1435                 new_access |= F_MDACC;
1436         } else {
1437                 if (desired_access & (ACE_READ_DATA | ACE_EXECUTE)) {
1438                         new_access |= F_RDACC;
1439                         flag |= FREAD;
1440                 }
1441 
1442                 if (desired_access & (ACE_WRITE_DATA | ACE_APPEND_DATA |
1443                     ACE_ADD_FILE)) {
1444                         new_access |= F_WRACC;
1445                         flag |= FWRITE;
1446                 }
1447 
1448                 if (SMB_DENY_READ(share_access)) {
1449                         deny |= F_RDDNY;
1450                 }
1451 
1452                 if (SMB_DENY_WRITE(share_access)) {
1453                         deny |= F_WRDNY;
1454                 }
1455 
1456                 if (cmd == F_SHARE_NBMAND) {
1457                         if (desired_access & ACE_DELETE)
1458                                 new_access |= F_RMACC;
1459 
1460                         if (SMB_DENY_DELETE(share_access)) {
1461                                 deny |= F_RMDNY;
1462                         }
1463                 }
1464         }
1465 
1466         shr.s_access = new_access;
1467         shr.s_deny = deny;
1468         shr.s_sysid = smb_ct.cc_sysid;
1469         shr.s_pid = uniq_fid;
1470         shr.s_own_len = sizeof (shr_own);
1471         shr.s_owner = (caddr_t)&shr_own;
1472         shr_own.sl_id = shr.s_sysid;
1473         shr_own.sl_pid = shr.s_pid;
1474 
1475         return (VOP_SHRLOCK(vp, cmd, &shr, flag, cr, NULL));
1476 }
1477 
1478 int
1479 smb_vop_unshrlock(vnode_t *vp, uint32_t uniq_fid, cred_t *cr)
1480 {
1481         struct shrlock shr;
1482         struct shr_locowner shr_own;
1483 
1484         /*
1485          * share locking is not supported for non-regular
1486          * objects in NBMAND mode.
1487          */
1488         if (nbl_need_check(vp) && (vp->v_type != VREG))
1489                 return (0);
1490 
1491         /*
1492          * For s_access and s_deny, we do not need to pass in the original
1493          * values.
1494          */
1495         shr.s_access = 0;
1496         shr.s_deny = 0;
1497         shr.s_sysid = smb_ct.cc_sysid;
1498         shr.s_pid = uniq_fid;
1499         shr.s_own_len = sizeof (shr_own);
1500         shr.s_owner = (caddr_t)&shr_own;
1501         shr_own.sl_id = shr.s_sysid;
1502         shr_own.sl_pid = shr.s_pid;
1503 
1504         return (VOP_SHRLOCK(vp, F_UNSHARE, &shr, 0, cr, NULL));
1505 }
1506 
1507 /*
1508  * Note about mandatory vs advisory locks:
1509  *
1510  * The SMB server really should always request mandatory locks, and
1511  * if the file system does not support them, the SMB server should
1512  * just tell the client it could not get the lock. If we were to
1513  * tell the SMB client "you got the lock" when what they really
1514  * got was only an advisory lock, we would be lying to the client
1515  * about their having exclusive access to the locked range, which
1516  * could easily lead to data corruption.  If someone really wants
1517  * the (dangerous) behavior they can set: smb_allow_advisory_locks
1518  */
1519 int
1520 smb_vop_frlock(vnode_t *vp, cred_t *cr, int flag, flock64_t *bf)
1521 {
1522         flk_callback_t flk_cb;
1523         int cmd = F_SETLK_NBMAND;
1524 
1525         if (smb_allow_advisory_locks != 0 && !nbl_need_check(vp)) {
1526                 /*
1527                  * The file system does not support nbmand, and
1528                  * smb_allow_advisory_locks is enabled. (danger!)
1529                  */
1530                 cmd = F_SETLK;
1531         }
1532 
1533         flk_init_callback(&flk_cb, smb_lock_frlock_callback, NULL);
1534 
1535         return (VOP_FRLOCK(vp, cmd, bf, flag, 0, &flk_cb, cr, &smb_ct));
1536 }
1537 
1538 static callb_cpr_t *
1539 /* ARGSUSED */
1540 smb_lock_frlock_callback(flk_cb_when_t when, void *error)
1541 {
1542         return (0);
1543 }
1544 
1545 /*
1546  * smb_vop_catia_init_v4_lookup
1547  * Initialize  mapping between wide characters in the range from
1548  * 0x00A4 to 0x00FF and their UNIX (v4) equivalent (wide character).
1549  * Indexed by the decimal value of the wide character (164-255)
1550  * with an offset of -164.
1551  */
1552 static void
1553 smb_vop_catia_init_v4_lookup()
1554 {
1555         int i, idx, offset = SMB_CATIA_V4_LOOKUP_LOW;
1556 
1557         for (i = 0; i < SMB_CATIA_V4_LOOKUP_MAX; i++)
1558                 smb_catia_v4_lookup[i] = (smb_wchar_t)(i + offset);
1559 
1560         for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) {
1561                 idx = (int)catia_maps[i].winchar - offset;
1562                 smb_catia_v4_lookup[idx] = (smb_wchar_t)catia_maps[i].unixchar;
1563         }
1564 }
1565 
1566 /*
1567  * smb_vop_catia_init_v5_lookup
1568  * Initialize mapping between UNIX ASCII (v4) characters and equivalent
1569  * or translated wide characters.
1570  * Indexed by the decimal value of the ASCII character (0-127).
1571  */
1572 static void
1573 smb_vop_catia_init_v5_lookup()
1574 {
1575         int i, idx;
1576 
1577         for (i = 0; i < SMB_CATIA_V5_LOOKUP_MAX; i++)
1578                 smb_catia_v5_lookup[i] = (smb_wchar_t)i;
1579 
1580         for (i = 0; i < SMB_CATIA_NUM_MAPS; i++) {
1581                 idx = (int)catia_maps[i].unixchar;
1582                 smb_catia_v5_lookup[idx] = catia_maps[i].winchar;
1583         }
1584 }
1585 
1586 static void
1587 smb_vop_catia_init()
1588 {
1589         smb_vop_catia_init_v4_lookup();
1590         smb_vop_catia_init_v5_lookup();
1591 }
1592 
1593 /*
1594  * smb_vop_catia_v5tov4
1595  * (windows (v5) to unix (v4))
1596  *
1597  * Traverse each character in the given source filename and convert the
1598  * multibyte that is equivalent to any special Windows character listed
1599  * in the catia_maps table to the Unix ASCII character if any is
1600  * encountered in the filename. The translated name is returned in buf.
1601  *
1602  * If an error occurs the conversion terminates and name is returned,
1603  * otherwise buf is returned.
1604  */
1605 char *
1606 smb_vop_catia_v5tov4(char *name, char *buf, int buflen)
1607 {
1608         int v4_idx, numbytes, inc;
1609         int space_left = buflen - 1; /* one byte reserved for null */
1610         uint32_t wc;
1611         char mbstring[MTS_MB_CHAR_MAX];
1612         char *p, *src = name, *dst = buf;
1613 
1614         ASSERT(name);
1615         ASSERT(buf);
1616 
1617         if (!buf || !name)
1618                 return (name);
1619 
1620         bzero(buf, buflen);
1621 
1622         while (*src) {
1623                 if ((numbytes = smb_mbtowc(&wc, src, MTS_MB_CHAR_MAX)) < 0)
1624                         return (name);
1625 
1626                 if (wc < SMB_CATIA_V4_LOOKUP_LOW ||
1627                     wc > SMB_CATIA_V4_LOOKUP_UPPER) {
1628                         inc = numbytes;
1629                         p = src;
1630                 } else {
1631                         /* Lookup required. */
1632                         v4_idx = (int)wc - SMB_CATIA_V4_LOOKUP_LOW;
1633                         inc = smb_wctomb(mbstring, smb_catia_v4_lookup[v4_idx]);
1634                         p = mbstring;
1635                 }
1636 
1637                 if (space_left < inc)
1638                         return (name);
1639 
1640                 (void) strncpy(dst, p, inc);
1641                 dst += inc;
1642                 space_left -= inc;
1643                 src += numbytes;
1644         }
1645 
1646         return (buf);
1647 }
1648 
1649 /*
1650  * smb_vop_catia_v4tov5
1651  * (unix (v4) to windows (v5))
1652  *
1653  * Traverse each character in the given filename 'srcbuf' and convert
1654  * the special Unix character that is listed in the catia_maps table to
1655  * the UTF-8 encoding of the corresponding Windows character if any is
1656  * encountered in the filename.
1657  *
1658  * The translated name is returned in buf.
1659  * If an error occurs the conversion terminates and the original name
1660  * is returned in buf.
1661  */
1662 void
1663 smb_vop_catia_v4tov5(char *name, char *buf, int buflen)
1664 {
1665         int v5_idx, numbytes;
1666         int space_left = buflen - 1; /* one byte reserved for null */
1667         uint32_t wc;
1668         char mbstring[MTS_MB_CHAR_MAX];
1669         char *src = name, *dst = buf;
1670 
1671         ASSERT(name);
1672         ASSERT(buf);
1673 
1674         if (!buf || !name)
1675                 return;
1676 
1677         (void) bzero(buf, buflen);
1678         while (*src) {
1679                 if (smb_isascii(*src)) {
1680                         /* Lookup required */
1681                         v5_idx = (int)*src++;
1682                         numbytes = smb_wctomb(mbstring,
1683                             smb_catia_v5_lookup[v5_idx]);
1684                         if (space_left < numbytes)
1685                                 break;
1686                         (void) strncpy(dst, mbstring, numbytes);
1687                 } else {
1688                         if ((numbytes = smb_mbtowc(&wc, src,
1689                             MTS_MB_CHAR_MAX)) < 0)
1690                                 break;
1691                         if (space_left < numbytes)
1692                                 break;
1693                         (void) strncpy(dst, src, numbytes);
1694                         src += numbytes;
1695                 }
1696 
1697                 dst += numbytes;
1698                 space_left -= numbytes;
1699         }
1700 
1701         if (*src)
1702                 (void) strlcpy(buf, name, buflen);
1703 }