Print this page
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
5404 smbfs needs mmap support
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Jason King <jason.brian.king@gmail.com>
Reviewed by: Andrew Stormont <andyjstormont@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
2552 smbfs: add support for NFS-like remove
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: Yuri Pankov <yuripv@yuripv.net>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: C Fraire <cfraire@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>


  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 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  26  *      All rights reserved.
  27  */
  28 /*
  29  * Copyright (c) 2017 by Delphix. All rights reserved.

  30  */
  31 
  32 /*
  33  * Node hash implementation initially borrowed from NFS (nfs_subr.c)
  34  * but then heavily modified. It's no longer an array of hash lists,
  35  * but an AVL tree per mount point.  More on this below.
  36  */
  37 
  38 #include <sys/param.h>
  39 #include <sys/systm.h>
  40 #include <sys/time.h>
  41 #include <sys/vnode.h>

  42 #include <sys/bitmap.h>

  43 #include <sys/dnlc.h>
  44 #include <sys/kmem.h>
  45 #include <sys/sunddi.h>
  46 #include <sys/sysmacros.h>

  47 
  48 #include <netsmb/smb_osdep.h>
  49 
  50 #include <netsmb/smb.h>
  51 #include <netsmb/smb_conn.h>
  52 #include <netsmb/smb_subr.h>
  53 #include <netsmb/smb_rq.h>
  54 
  55 #include <smbfs/smbfs.h>
  56 #include <smbfs/smbfs_node.h>
  57 #include <smbfs/smbfs_subr.h>
  58 
  59 /*
  60  * The AVL trees (now per-mount) allow finding an smbfs node by its
  61  * full remote path name.  It also allows easy traversal of all nodes
  62  * below (path wise) any given node.  A reader/writer lock for each
  63  * (per mount) AVL tree is used to control access and to synchronize
  64  * lookups, additions, and deletions from that AVL tree.
  65  *
  66  * Previously, this code use a global array of hash chains, each with


 134  * Local functions.
 135  * SN for Smb Node
 136  */
 137 static void sn_rmfree(smbnode_t *);
 138 static void sn_inactive(smbnode_t *);
 139 static void sn_addhash_locked(smbnode_t *, avl_index_t);
 140 static void sn_rmhash_locked(smbnode_t *);
 141 static void sn_destroy_node(smbnode_t *);
 142 void smbfs_kmem_reclaim(void *cdrarg);
 143 
 144 static smbnode_t *
 145 sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *);
 146 
 147 static smbnode_t *
 148 make_smbnode(smbmntinfo_t *, const char *, int, int *);
 149 
 150 /*
 151  * Free the resources associated with an smbnode.
 152  * Note: This is different from smbfs_inactive
 153  *
 154  * NFS: nfs_subr.c:rinactive
 155  */
 156 static void
 157 sn_inactive(smbnode_t *np)
 158 {
 159         vsecattr_t      ovsa;
 160         cred_t          *oldcr;
 161         char            *orpath;
 162         int             orplen;

 163 
 164         /*
 165          * Flush and invalidate all pages (todo)

 166          * Free any held credentials and caches...
 167          * etc.  (See NFS code)
 168          */
 169         mutex_enter(&np->r_statelock);
 170 
 171         ovsa = np->r_secattr;
 172         np->r_secattr = smbfs_vsa0;
 173         np->r_sectime = 0;
 174 
 175         oldcr = np->r_cred;
 176         np->r_cred = NULL;
 177 
 178         orpath = np->n_rpath;
 179         orplen = np->n_rplen;
 180         np->n_rpath = NULL;
 181         np->n_rplen = 0;
 182 
 183         mutex_exit(&np->r_statelock);
 184 





 185         if (ovsa.vsa_aclentp != NULL)
 186                 kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
 187 
 188         if (oldcr != NULL)
 189                 crfree(oldcr);
 190 
 191         if (orpath != NULL)
 192                 kmem_free(orpath, orplen + 1);
 193 }
 194 
 195 /*
 196  * Find and optionally create an smbnode for the passed
 197  * mountinfo, directory, separator, and name.  If the
 198  * desired smbnode already exists, return a reference.
 199  * If the file attributes pointer is non-null, the node
 200  * is created if necessary and linked into the AVL tree.
 201  *
 202  * Callers that need a node created but don't have the
 203  * real attributes pass smbfs_fattr0 to force creation.
 204  *
 205  * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
 206  *
 207  * NFS: nfs_subr.c:makenfsnode
 208  */
 209 smbnode_t *
 210 smbfs_node_findcreate(
 211         smbmntinfo_t *mi,
 212         const char *dirnm,
 213         int dirlen,
 214         const char *name,
 215         int nmlen,
 216         char sep,
 217         struct smbfattr *fap)
 218 {
 219         char tmpbuf[256];
 220         size_t rpalloc;
 221         char *p, *rpath;
 222         int rplen;
 223         smbnode_t *np;
 224         vnode_t *vp;
 225         int newnode;
 226 
 227         /*


 269                  * Caller is "just looking" (no create)
 270                  * so np may or may not be NULL here.
 271                  * Either way, we're done.
 272                  */
 273                 return (np);
 274         }
 275 
 276         /*
 277          * We should have a node, possibly created.
 278          * Do we have (real) attributes to apply?
 279          */
 280         ASSERT(np != NULL);
 281         if (fap == &smbfs_fattr0)
 282                 return (np);
 283 
 284         /*
 285          * Apply the given attributes to this node,
 286          * dealing with any cache impact, etc.
 287          */
 288         vp = SMBTOV(np);
 289         if (!newnode) {
 290                 /*
 291                  * Found an existing node.
 292                  * Maybe purge caches...
 293                  */
 294                 smbfs_cache_check(vp, fap);
 295         }
 296         smbfs_attrcache_fa(vp, fap);
 297 
 298         /*
 299          * Note NFS sets vp->v_type here, assuming it
 300          * can never change for the life of a node.
 301          * We allow v_type to change, and set it in
 302          * smbfs_attrcache().  Also: mode, uid, gid
 303          */
 304         return (np);
 305 }
 306 
 307 /*
 308  * NFS: nfs_subr.c:rtablehash
 309  * We use smbfs_hash().
 310  */
 311 
 312 /*
 313  * Find or create an smbnode.
 314  * NFS: nfs_subr.c:make_rnode
 315  */
 316 static smbnode_t *
 317 make_smbnode(
 318         smbmntinfo_t *mi,
 319         const char *rpath,
 320         int rplen,
 321         int *newnode)
 322 {
 323         smbnode_t *np;
 324         smbnode_t *tnp;
 325         vnode_t *vp;
 326         vfs_t *vfsp;
 327         avl_index_t where;
 328         char *new_rpath = NULL;
 329 
 330         ASSERT(RW_READ_HELD(&mi->smi_hash_lk));
 331         vfsp = mi->smi_vfsp;
 332 
 333 start:
 334         np = sn_hashfind(mi, rpath, rplen, NULL);


 412 
 413         /*
 414          * Allocate and copy the rpath we'll need below.
 415          */
 416         new_rpath = kmem_alloc(rplen + 1, KM_SLEEP);
 417         bcopy(rpath, new_rpath, rplen);
 418         new_rpath[rplen] = '\0';
 419 
 420         /* Initialize smbnode_t */
 421         bzero(np, sizeof (*np));
 422 
 423         smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
 424         smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
 425         mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
 426         cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
 427         /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
 428 
 429         np->r_vnode = vp;
 430         np->n_mount = mi;
 431 
 432         np->n_fid = SMB_FID_UNUSED;
 433         np->n_uid = mi->smi_uid;
 434         np->n_gid = mi->smi_gid;
 435         /* Leave attributes "stale." */
 436 
 437 #if 0 /* XXX dircache */
 438         /*
 439          * We don't know if it's a directory yet.
 440          * Let the caller do this?  XXX
 441          */
 442         avl_create(&np->r_dir, compar, sizeof (rddir_cache),
 443             offsetof(rddir_cache, tree));
 444 #endif
 445 
 446         /* Now fill in the vnode. */
 447         vn_setops(vp, smbfs_vnodeops);
 448         vp->v_data = (caddr_t)np;
 449         VFS_HOLD(vfsp);
 450         vp->v_vfsp = vfsp;
 451         vp->v_type = VNON;
 452 
 453         /*
 454          * We entered with mi->smi_hash_lk held (reader).
 455          * Retake it now, (as the writer).
 456          * Will return with it held.
 457          */
 458         rw_enter(&mi->smi_hash_lk, RW_WRITER);
 459 
 460         /*
 461          * There is a race condition where someone else
 462          * may alloc the smbnode while no locks are held,
 463          * so check again and recover if found.
 464          */


 482          * this node into the node cache (AVL tree).
 483          */
 484         np->n_rpath = new_rpath;
 485         np->n_rplen = rplen;
 486         np->n_ino = smbfs_gethash(new_rpath, rplen);
 487 
 488         sn_addhash_locked(np, where);
 489         *newnode = 1;
 490         return (np);
 491 }
 492 
 493 /*
 494  * smbfs_addfree
 495  * Put an smbnode on the free list, or destroy it immediately
 496  * if it offers no value were it to be reclaimed later.  Also
 497  * destroy immediately when we have too many smbnodes, etc.
 498  *
 499  * Normally called by smbfs_inactive, but also
 500  * called in here during cleanup operations.
 501  *
 502  * NFS: nfs_subr.c:rp_addfree
 503  */
 504 void
 505 smbfs_addfree(smbnode_t *np)
 506 {
 507         vnode_t *vp;
 508         struct vfs *vfsp;
 509         smbmntinfo_t *mi;
 510 
 511         ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
 512 
 513         vp = SMBTOV(np);
 514         ASSERT(vp->v_count >= 1);
 515 
 516         vfsp = vp->v_vfsp;
 517         mi = VFTOSMI(vfsp);
 518 
 519         /*
 520          * If there are no more references to this smbnode and:
 521          * we have too many smbnodes allocated, or if the node
 522          * is no longer accessible via the AVL tree (!RHASHED),


 610                 np->r_freef = np;
 611                 np->r_freeb = np;
 612                 smbfreelist = np;
 613         } else {
 614                 np->r_freef = smbfreelist;
 615                 np->r_freeb = smbfreelist->r_freeb;
 616                 smbfreelist->r_freeb->r_freef = np;
 617                 smbfreelist->r_freeb = np;
 618         }
 619         mutex_exit(&smbfreelist_lock);
 620 
 621         rw_exit(&mi->smi_hash_lk);
 622 }
 623 
 624 /*
 625  * Remove an smbnode from the free list.
 626  *
 627  * The caller must be holding smbfreelist_lock and the smbnode
 628  * must be on the freelist.
 629  *
 630  * NFS: nfs_subr.c:rp_rmfree
 631  */
 632 static void
 633 sn_rmfree(smbnode_t *np)
 634 {
 635 
 636         ASSERT(MUTEX_HELD(&smbfreelist_lock));
 637         ASSERT(np->r_freef != NULL && np->r_freeb != NULL);
 638 
 639         if (np == smbfreelist) {
 640                 smbfreelist = np->r_freef;
 641                 if (np == smbfreelist)
 642                         smbfreelist = NULL;
 643         }
 644 
 645         np->r_freeb->r_freef = np->r_freef;
 646         np->r_freef->r_freeb = np->r_freeb;
 647 
 648         np->r_freef = np->r_freeb = NULL;
 649 }
 650 
 651 /*
 652  * Put an smbnode in the "hash" AVL tree.
 653  *
 654  * The caller must be hold the rwlock as writer.
 655  *
 656  * NFS: nfs_subr.c:rp_addhash
 657  */
 658 static void
 659 sn_addhash_locked(smbnode_t *np, avl_index_t where)
 660 {
 661         smbmntinfo_t *mi = np->n_mount;
 662 
 663         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
 664         ASSERT(!(np->r_flags & RHASHED));
 665 
 666         avl_insert(&mi->smi_hash_avl, np, where);
 667 
 668         mutex_enter(&np->r_statelock);


 669         np->r_flags |= RHASHED;

 670         mutex_exit(&np->r_statelock);
 671 }
 672 
 673 /*
 674  * Remove an smbnode from the "hash" AVL tree.
 675  *
 676  * The caller must hold the rwlock as writer.
 677  *
 678  * NFS: nfs_subr.c:rp_rmhash_locked
 679  */
 680 static void
 681 sn_rmhash_locked(smbnode_t *np)
 682 {
 683         smbmntinfo_t *mi = np->n_mount;
 684 
 685         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
 686         ASSERT(np->r_flags & RHASHED);
 687 
 688         avl_remove(&mi->smi_hash_avl, np);
 689 
 690         mutex_enter(&np->r_statelock);

 691         np->r_flags &= ~RHASHED;


 692         mutex_exit(&np->r_statelock);
 693 }
 694 
 695 /*
 696  * Remove an smbnode from the "hash" AVL tree.
 697  *
 698  * The caller must not be holding the rwlock.
 699  */
 700 void
 701 smbfs_rmhash(smbnode_t *np)
 702 {
 703         smbmntinfo_t *mi = np->n_mount;
 704 
 705         rw_enter(&mi->smi_hash_lk, RW_WRITER);
 706         sn_rmhash_locked(np);
 707         rw_exit(&mi->smi_hash_lk);
 708 }
 709 
 710 /*
 711  * Lookup an smbnode by remote pathname
 712  *
 713  * The caller must be holding the AVL rwlock, either shared or exclusive.
 714  *
 715  * NFS: nfs_subr.c:rfind
 716  */
 717 static smbnode_t *
 718 sn_hashfind(
 719         smbmntinfo_t *mi,
 720         const char *rpath,
 721         int rplen,
 722         avl_index_t *pwhere) /* optional */
 723 {
 724         smbfs_node_hdr_t nhdr;
 725         smbnode_t *np;
 726         vnode_t *vp;
 727 
 728         ASSERT(RW_LOCK_HELD(&mi->smi_hash_lk));
 729 
 730         bzero(&nhdr, sizeof (nhdr));
 731         nhdr.hdr_n_rpath = (char *)rpath;
 732         nhdr.hdr_n_rplen = rplen;
 733 
 734         /* See smbfs_node_cmp below. */
 735         np = avl_find(&mi->smi_hash_avl, &nhdr, pwhere);


 850 
 851         rw_exit(&mi->smi_hash_lk);
 852 }
 853 
 854 #ifdef SMB_VNODE_DEBUG
 855 int smbfs_check_table_debug = 1;
 856 #else /* SMB_VNODE_DEBUG */
 857 int smbfs_check_table_debug = 0;
 858 #endif /* SMB_VNODE_DEBUG */
 859 
 860 
 861 /*
 862  * Return 1 if there is a active vnode belonging to this vfs in the
 863  * smbnode cache.
 864  *
 865  * Several of these checks are done without holding the usual
 866  * locks.  This is safe because destroy_smbtable(), smbfs_addfree(),
 867  * etc. will redo the necessary checks before actually destroying
 868  * any smbnodes.
 869  *
 870  * NFS: nfs_subr.c:check_rtable
 871  *
 872  * Debugging changes here relative to NFS.
 873  * Relatively harmless, so left 'em in.
 874  */
 875 int
 876 smbfs_check_table(struct vfs *vfsp, smbnode_t *rtnp)
 877 {
 878         smbmntinfo_t *mi;
 879         smbnode_t *np;
 880         vnode_t *vp;
 881         int busycnt = 0;
 882 
 883         mi = VFTOSMI(vfsp);
 884         rw_enter(&mi->smi_hash_lk, RW_READER);
 885         for (np = avl_first(&mi->smi_hash_avl); np != NULL;
 886             np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
 887 
 888                 if (np == rtnp)
 889                         continue; /* skip the root */
 890                 vp = SMBTOV(np);


 909                 if (np->r_count > 0) {
 910                         SMBVDEBUG("+r_count: node=0x%p, rpath=%s\n",
 911                             (void *)np, np->n_rpath);
 912                         busycnt++;
 913                 }
 914 
 915                 if (busycnt && !smbfs_check_table_debug)
 916                         break;
 917 
 918         }
 919         rw_exit(&mi->smi_hash_lk);
 920 
 921         return (busycnt);
 922 }
 923 
 924 /*
 925  * Destroy inactive vnodes from the AVL tree which belong to this
 926  * vfs.  It is essential that we destroy all inactive vnodes during a
 927  * forced unmount as well as during a normal unmount.
 928  *
 929  * NFS: nfs_subr.c:destroy_rtable
 930  *
 931  * In here, we're normally destrying all or most of the AVL tree,
 932  * so the natural choice is to use avl_destroy_nodes.  However,
 933  * there may be a few busy nodes that should remain in the AVL
 934  * tree when we're done.  The solution: use a temporary tree to
 935  * hold the busy nodes until we're done destroying the old tree,
 936  * then copy the temporary tree over the (now emtpy) real tree.
 937  */
 938 void
 939 smbfs_destroy_table(struct vfs *vfsp)
 940 {
 941         avl_tree_t tmp_avl;
 942         smbmntinfo_t *mi;
 943         smbnode_t *np;
 944         smbnode_t *rlist;
 945         void *v;
 946 
 947         mi = VFTOSMI(vfsp);
 948         rlist = NULL;
 949         smbfs_init_hash_avl(&tmp_avl);


 994          */
 995         mi->smi_hash_avl = tmp_avl;
 996         rw_exit(&mi->smi_hash_lk);
 997 
 998         /*
 999          * Now destroy the nodes on our temporary list (rlist).
1000          * This call to smbfs_addfree will end up destroying the
1001          * smbnode, but in a safe way with the appropriate set
1002          * of checks done.
1003          */
1004         while ((np = rlist) != NULL) {
1005                 rlist = (smbnode_t *)np->r_avl_node.avl_child[0];
1006                 smbfs_addfree(np);
1007         }
1008 }
1009 
1010 /*
1011  * This routine destroys all the resources associated with the smbnode
1012  * and then the smbnode itself.  Note: sn_inactive has been called.
1013  *
1014  * NFS: nfs_subr.c:destroy_rnode
1015  */
1016 static void
1017 sn_destroy_node(smbnode_t *np)
1018 {
1019         vnode_t *vp;
1020         vfs_t *vfsp;
1021 
1022         vp = SMBTOV(np);
1023         vfsp = vp->v_vfsp;
1024 
1025         ASSERT(vp->v_count == 1);
1026         ASSERT(np->r_count == 0);
1027         ASSERT(np->r_mapcnt == 0);
1028         ASSERT(np->r_secattr.vsa_aclentp == NULL);
1029         ASSERT(np->r_cred == NULL);
1030         ASSERT(np->n_rpath == NULL);
1031         ASSERT(!(np->r_flags & RHASHED));
1032         ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
1033         atomic_dec_ulong((ulong_t *)&smbnodenew);
1034         vn_invalid(vp);
1035         vn_free(vp);
1036         kmem_cache_free(smbnode_cache, np);
1037         VFS_RELE(vfsp);
1038 }
1039 
1040 /*

1041  * Flush all vnodes in this (or every) vfs.
1042  * Used by nfs_sync and by nfs_unmount.
1043  */
1044 /*ARGSUSED*/
1045 void
1046 smbfs_rflush(struct vfs *vfsp, cred_t *cr)
1047 {
1048         /* Todo: mmap support. */





























































1049 }
1050 
1051 /* access cache */
1052 /* client handles */
1053 
1054 /*







































































































1055  * initialize resources that are used by smbfs_subr.c
1056  * this is called from the _init() routine (by the way of smbfs_clntinit())
1057  *
1058  * NFS: nfs_subr.c:nfs_subrinit
1059  */
1060 int
1061 smbfs_subrinit(void)
1062 {
1063         ulong_t nsmbnode_max;
1064 
1065         /*
1066          * Allocate and initialize the smbnode cache
1067          */
1068         if (nsmbnode <= 0)
1069                 nsmbnode = ncsize; /* dnlc.h */
1070         nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
1071             sizeof (struct smbnode));
1072         if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
1073                 zcmn_err(GLOBAL_ZONEID, CE_NOTE,
1074                     "setting nsmbnode to max value of %ld", nsmbnode_max);
1075                 nsmbnode = nsmbnode_max;
1076         }
1077 
1078         smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
1079             0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
1080 
1081         /*
1082          * Initialize the various mutexes and reader/writer locks
1083          */
1084         mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
1085         mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
1086 
1087         /*
1088          * Assign unique major number for all smbfs mounts
1089          */
1090         if ((smbfs_major = getudev()) == -1) {
1091                 zcmn_err(GLOBAL_ZONEID, CE_WARN,
1092                     "smbfs: init: can't get unique device number");
1093                 smbfs_major = 0;
1094         }
1095         smbfs_minor = 0;
1096 
1097         return (0);
1098 }
1099 
1100 /*
1101  * free smbfs hash table, etc.
1102  * NFS: nfs_subr.c:nfs_subrfini
1103  */
1104 void
1105 smbfs_subrfini(void)
1106 {
1107 
1108         /*
1109          * Destroy the smbnode cache
1110          */
1111         kmem_cache_destroy(smbnode_cache);
1112 
1113         /*
1114          * Destroy the various mutexes and reader/writer locks
1115          */
1116         mutex_destroy(&smbfreelist_lock);
1117         mutex_destroy(&smbfs_minor_lock);
1118 }
1119 
1120 /* rddir_cache ? */
1121 
1122 /*


1157                  */
1158                 smbfs_addfree(np);
1159                 mutex_enter(&smbfreelist_lock);
1160         }
1161         mutex_exit(&smbfreelist_lock);
1162 }
1163 
1164 /*
1165  * Called by kmem_cache_alloc ask us if we could
1166  * "Please give back some memory!"
1167  *
1168  * Todo: dump nodes from the free list?
1169  */
1170 /*ARGSUSED*/
1171 void
1172 smbfs_kmem_reclaim(void *cdrarg)
1173 {
1174         smbfs_node_reclaim();
1175 }
1176 
1177 /* nfs failover stuff */
1178 /* nfs_rw_xxx - see smbfs_rwlock.c */




  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 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  26  *      All rights reserved.
  27  */
  28 /*
  29  * Copyright (c) 2017 by Delphix. All rights reserved.
  30  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  31  */
  32 
  33 /*
  34  * Node hash implementation initially borrowed from NFS (nfs_subr.c)
  35  * but then heavily modified. It's no longer an array of hash lists,
  36  * but an AVL tree per mount point.  More on this below.
  37  */
  38 
  39 #include <sys/param.h>
  40 #include <sys/systm.h>
  41 #include <sys/time.h>
  42 #include <sys/vnode.h>
  43 #include <sys/atomic.h>
  44 #include <sys/bitmap.h>
  45 #include <sys/buf.h>
  46 #include <sys/dnlc.h>
  47 #include <sys/kmem.h>
  48 #include <sys/sunddi.h>
  49 #include <sys/sysmacros.h>
  50 #include <sys/fcntl.h>
  51 
  52 #include <netsmb/smb_osdep.h>
  53 
  54 #include <netsmb/smb.h>
  55 #include <netsmb/smb_conn.h>
  56 #include <netsmb/smb_subr.h>
  57 #include <netsmb/smb_rq.h>
  58 
  59 #include <smbfs/smbfs.h>
  60 #include <smbfs/smbfs_node.h>
  61 #include <smbfs/smbfs_subr.h>
  62 
  63 /*
  64  * The AVL trees (now per-mount) allow finding an smbfs node by its
  65  * full remote path name.  It also allows easy traversal of all nodes
  66  * below (path wise) any given node.  A reader/writer lock for each
  67  * (per mount) AVL tree is used to control access and to synchronize
  68  * lookups, additions, and deletions from that AVL tree.
  69  *
  70  * Previously, this code use a global array of hash chains, each with


 138  * Local functions.
 139  * SN for Smb Node
 140  */
 141 static void sn_rmfree(smbnode_t *);
 142 static void sn_inactive(smbnode_t *);
 143 static void sn_addhash_locked(smbnode_t *, avl_index_t);
 144 static void sn_rmhash_locked(smbnode_t *);
 145 static void sn_destroy_node(smbnode_t *);
 146 void smbfs_kmem_reclaim(void *cdrarg);
 147 
 148 static smbnode_t *
 149 sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *);
 150 
 151 static smbnode_t *
 152 make_smbnode(smbmntinfo_t *, const char *, int, int *);
 153 
 154 /*
 155  * Free the resources associated with an smbnode.
 156  * Note: This is different from smbfs_inactive
 157  *
 158  * From NFS: nfs_subr.c:rinactive
 159  */
 160 static void
 161 sn_inactive(smbnode_t *np)
 162 {
 163         vsecattr_t      ovsa;
 164         cred_t          *oldcr;
 165         char            *orpath;
 166         int             orplen;
 167         vnode_t         *vp;
 168 
 169         /*
 170          * Here NFS has:
 171          * Flush and invalidate all pages (done by caller)
 172          * Free any held credentials and caches...
 173          * etc.  (See NFS code)
 174          */
 175         mutex_enter(&np->r_statelock);
 176 
 177         ovsa = np->r_secattr;
 178         np->r_secattr = smbfs_vsa0;
 179         np->r_sectime = 0;
 180 
 181         oldcr = np->r_cred;
 182         np->r_cred = NULL;
 183 
 184         orpath = np->n_rpath;
 185         orplen = np->n_rplen;
 186         np->n_rpath = NULL;
 187         np->n_rplen = 0;
 188 
 189         mutex_exit(&np->r_statelock);
 190 
 191         vp = SMBTOV(np);
 192         if (vn_has_cached_data(vp)) {
 193                 ASSERT3P(vp,==,NULL);
 194         }
 195 
 196         if (ovsa.vsa_aclentp != NULL)
 197                 kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
 198 
 199         if (oldcr != NULL)
 200                 crfree(oldcr);
 201 
 202         if (orpath != NULL)
 203                 kmem_free(orpath, orplen + 1);
 204 }
 205 
 206 /*
 207  * Find and optionally create an smbnode for the passed
 208  * mountinfo, directory, separator, and name.  If the
 209  * desired smbnode already exists, return a reference.
 210  * If the file attributes pointer is non-null, the node
 211  * is created if necessary and linked into the AVL tree.
 212  *
 213  * Callers that need a node created but don't have the
 214  * real attributes pass smbfs_fattr0 to force creation.
 215  *
 216  * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
 217  *
 218  * Based on NFS: nfs_subr.c:makenfsnode
 219  */
 220 smbnode_t *
 221 smbfs_node_findcreate(
 222         smbmntinfo_t *mi,
 223         const char *dirnm,
 224         int dirlen,
 225         const char *name,
 226         int nmlen,
 227         char sep,
 228         struct smbfattr *fap)
 229 {
 230         char tmpbuf[256];
 231         size_t rpalloc;
 232         char *p, *rpath;
 233         int rplen;
 234         smbnode_t *np;
 235         vnode_t *vp;
 236         int newnode;
 237 
 238         /*


 280                  * Caller is "just looking" (no create)
 281                  * so np may or may not be NULL here.
 282                  * Either way, we're done.
 283                  */
 284                 return (np);
 285         }
 286 
 287         /*
 288          * We should have a node, possibly created.
 289          * Do we have (real) attributes to apply?
 290          */
 291         ASSERT(np != NULL);
 292         if (fap == &smbfs_fattr0)
 293                 return (np);
 294 
 295         /*
 296          * Apply the given attributes to this node,
 297          * dealing with any cache impact, etc.
 298          */
 299         vp = SMBTOV(np);







 300         smbfs_attrcache_fa(vp, fap);
 301 
 302         /*
 303          * Note NFS sets vp->v_type here, assuming it
 304          * can never change for the life of a node.
 305          * We allow v_type to change, and set it in
 306          * smbfs_attrcache().  Also: mode, uid, gid
 307          */
 308         return (np);
 309 }
 310 
 311 /*
 312  * Here NFS has: nfs_subr.c:rtablehash
 313  * We use smbfs_hash().
 314  */
 315 
 316 /*
 317  * Find or create an smbnode.
 318  * From NFS: nfs_subr.c:make_rnode
 319  */
 320 static smbnode_t *
 321 make_smbnode(
 322         smbmntinfo_t *mi,
 323         const char *rpath,
 324         int rplen,
 325         int *newnode)
 326 {
 327         smbnode_t *np;
 328         smbnode_t *tnp;
 329         vnode_t *vp;
 330         vfs_t *vfsp;
 331         avl_index_t where;
 332         char *new_rpath = NULL;
 333 
 334         ASSERT(RW_READ_HELD(&mi->smi_hash_lk));
 335         vfsp = mi->smi_vfsp;
 336 
 337 start:
 338         np = sn_hashfind(mi, rpath, rplen, NULL);


 416 
 417         /*
 418          * Allocate and copy the rpath we'll need below.
 419          */
 420         new_rpath = kmem_alloc(rplen + 1, KM_SLEEP);
 421         bcopy(rpath, new_rpath, rplen);
 422         new_rpath[rplen] = '\0';
 423 
 424         /* Initialize smbnode_t */
 425         bzero(np, sizeof (*np));
 426 
 427         smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
 428         smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
 429         mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
 430         cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
 431         /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
 432 
 433         np->r_vnode = vp;
 434         np->n_mount = mi;
 435 
 436         np->n_fid = NULL;
 437         np->n_uid = mi->smi_uid;
 438         np->n_gid = mi->smi_gid;
 439         /* Leave attributes "stale." */
 440 

 441         /*
 442          * Here NFS has avl_create(&np->r_dir, ...)
 443          * for the readdir cache (not used here).
 444          */



 445 
 446         /* Now fill in the vnode. */
 447         vn_setops(vp, smbfs_vnodeops);
 448         vp->v_data = (caddr_t)np;
 449         VFS_HOLD(vfsp);
 450         vp->v_vfsp = vfsp;
 451         vp->v_type = VNON;
 452 
 453         /*
 454          * We entered with mi->smi_hash_lk held (reader).
 455          * Retake it now, (as the writer).
 456          * Will return with it held.
 457          */
 458         rw_enter(&mi->smi_hash_lk, RW_WRITER);
 459 
 460         /*
 461          * There is a race condition where someone else
 462          * may alloc the smbnode while no locks are held,
 463          * so check again and recover if found.
 464          */


 482          * this node into the node cache (AVL tree).
 483          */
 484         np->n_rpath = new_rpath;
 485         np->n_rplen = rplen;
 486         np->n_ino = smbfs_gethash(new_rpath, rplen);
 487 
 488         sn_addhash_locked(np, where);
 489         *newnode = 1;
 490         return (np);
 491 }
 492 
 493 /*
 494  * smbfs_addfree
 495  * Put an smbnode on the free list, or destroy it immediately
 496  * if it offers no value were it to be reclaimed later.  Also
 497  * destroy immediately when we have too many smbnodes, etc.
 498  *
 499  * Normally called by smbfs_inactive, but also
 500  * called in here during cleanup operations.
 501  *
 502  * From NFS: nfs_subr.c:rp_addfree
 503  */
 504 void
 505 smbfs_addfree(smbnode_t *np)
 506 {
 507         vnode_t *vp;
 508         struct vfs *vfsp;
 509         smbmntinfo_t *mi;
 510 
 511         ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
 512 
 513         vp = SMBTOV(np);
 514         ASSERT(vp->v_count >= 1);
 515 
 516         vfsp = vp->v_vfsp;
 517         mi = VFTOSMI(vfsp);
 518 
 519         /*
 520          * If there are no more references to this smbnode and:
 521          * we have too many smbnodes allocated, or if the node
 522          * is no longer accessible via the AVL tree (!RHASHED),


 610                 np->r_freef = np;
 611                 np->r_freeb = np;
 612                 smbfreelist = np;
 613         } else {
 614                 np->r_freef = smbfreelist;
 615                 np->r_freeb = smbfreelist->r_freeb;
 616                 smbfreelist->r_freeb->r_freef = np;
 617                 smbfreelist->r_freeb = np;
 618         }
 619         mutex_exit(&smbfreelist_lock);
 620 
 621         rw_exit(&mi->smi_hash_lk);
 622 }
 623 
 624 /*
 625  * Remove an smbnode from the free list.
 626  *
 627  * The caller must be holding smbfreelist_lock and the smbnode
 628  * must be on the freelist.
 629  *
 630  * From NFS: nfs_subr.c:rp_rmfree
 631  */
 632 static void
 633 sn_rmfree(smbnode_t *np)
 634 {
 635 
 636         ASSERT(MUTEX_HELD(&smbfreelist_lock));
 637         ASSERT(np->r_freef != NULL && np->r_freeb != NULL);
 638 
 639         if (np == smbfreelist) {
 640                 smbfreelist = np->r_freef;
 641                 if (np == smbfreelist)
 642                         smbfreelist = NULL;
 643         }
 644 
 645         np->r_freeb->r_freef = np->r_freef;
 646         np->r_freef->r_freeb = np->r_freeb;
 647 
 648         np->r_freef = np->r_freeb = NULL;
 649 }
 650 
 651 /*
 652  * Put an smbnode in the "hash" AVL tree.
 653  *
 654  * The caller must be hold the rwlock as writer.
 655  *
 656  * From NFS: nfs_subr.c:rp_addhash
 657  */
 658 static void
 659 sn_addhash_locked(smbnode_t *np, avl_index_t where)
 660 {
 661         smbmntinfo_t *mi = np->n_mount;
 662 
 663         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));

 664 


 665         mutex_enter(&np->r_statelock);
 666         if ((np->r_flags & RHASHED) == 0) {
 667                 avl_insert(&mi->smi_hash_avl, np, where);
 668                 np->r_flags |= RHASHED;
 669         }
 670         mutex_exit(&np->r_statelock);
 671 }
 672 
 673 /*
 674  * Remove an smbnode from the "hash" AVL tree.
 675  *
 676  * The caller must hold the rwlock as writer.
 677  *
 678  * From NFS: nfs_subr.c:rp_rmhash_locked
 679  */
 680 static void
 681 sn_rmhash_locked(smbnode_t *np)
 682 {
 683         smbmntinfo_t *mi = np->n_mount;
 684 
 685         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));

 686 


 687         mutex_enter(&np->r_statelock);
 688         if ((np->r_flags & RHASHED) != 0) {
 689                 np->r_flags &= ~RHASHED;
 690                 avl_remove(&mi->smi_hash_avl, np);
 691         }
 692         mutex_exit(&np->r_statelock);
 693 }
 694 
 695 /*
 696  * Remove an smbnode from the "hash" AVL tree.
 697  *
 698  * The caller must not be holding the rwlock.
 699  */
 700 void
 701 smbfs_rmhash(smbnode_t *np)
 702 {
 703         smbmntinfo_t *mi = np->n_mount;
 704 
 705         rw_enter(&mi->smi_hash_lk, RW_WRITER);
 706         sn_rmhash_locked(np);
 707         rw_exit(&mi->smi_hash_lk);
 708 }
 709 
 710 /*
 711  * Lookup an smbnode by remote pathname
 712  *
 713  * The caller must be holding the AVL rwlock, either shared or exclusive.
 714  *
 715  * From NFS: nfs_subr.c:rfind
 716  */
 717 static smbnode_t *
 718 sn_hashfind(
 719         smbmntinfo_t *mi,
 720         const char *rpath,
 721         int rplen,
 722         avl_index_t *pwhere) /* optional */
 723 {
 724         smbfs_node_hdr_t nhdr;
 725         smbnode_t *np;
 726         vnode_t *vp;
 727 
 728         ASSERT(RW_LOCK_HELD(&mi->smi_hash_lk));
 729 
 730         bzero(&nhdr, sizeof (nhdr));
 731         nhdr.hdr_n_rpath = (char *)rpath;
 732         nhdr.hdr_n_rplen = rplen;
 733 
 734         /* See smbfs_node_cmp below. */
 735         np = avl_find(&mi->smi_hash_avl, &nhdr, pwhere);


 850 
 851         rw_exit(&mi->smi_hash_lk);
 852 }
 853 
 854 #ifdef SMB_VNODE_DEBUG
 855 int smbfs_check_table_debug = 1;
 856 #else /* SMB_VNODE_DEBUG */
 857 int smbfs_check_table_debug = 0;
 858 #endif /* SMB_VNODE_DEBUG */
 859 
 860 
 861 /*
 862  * Return 1 if there is a active vnode belonging to this vfs in the
 863  * smbnode cache.
 864  *
 865  * Several of these checks are done without holding the usual
 866  * locks.  This is safe because destroy_smbtable(), smbfs_addfree(),
 867  * etc. will redo the necessary checks before actually destroying
 868  * any smbnodes.
 869  *
 870  * From NFS: nfs_subr.c:check_rtable
 871  *
 872  * Debugging changes here relative to NFS.
 873  * Relatively harmless, so left 'em in.
 874  */
 875 int
 876 smbfs_check_table(struct vfs *vfsp, smbnode_t *rtnp)
 877 {
 878         smbmntinfo_t *mi;
 879         smbnode_t *np;
 880         vnode_t *vp;
 881         int busycnt = 0;
 882 
 883         mi = VFTOSMI(vfsp);
 884         rw_enter(&mi->smi_hash_lk, RW_READER);
 885         for (np = avl_first(&mi->smi_hash_avl); np != NULL;
 886             np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
 887 
 888                 if (np == rtnp)
 889                         continue; /* skip the root */
 890                 vp = SMBTOV(np);


 909                 if (np->r_count > 0) {
 910                         SMBVDEBUG("+r_count: node=0x%p, rpath=%s\n",
 911                             (void *)np, np->n_rpath);
 912                         busycnt++;
 913                 }
 914 
 915                 if (busycnt && !smbfs_check_table_debug)
 916                         break;
 917 
 918         }
 919         rw_exit(&mi->smi_hash_lk);
 920 
 921         return (busycnt);
 922 }
 923 
 924 /*
 925  * Destroy inactive vnodes from the AVL tree which belong to this
 926  * vfs.  It is essential that we destroy all inactive vnodes during a
 927  * forced unmount as well as during a normal unmount.
 928  *
 929  * Based on NFS: nfs_subr.c:destroy_rtable
 930  *
 931  * In here, we're normally destrying all or most of the AVL tree,
 932  * so the natural choice is to use avl_destroy_nodes.  However,
 933  * there may be a few busy nodes that should remain in the AVL
 934  * tree when we're done.  The solution: use a temporary tree to
 935  * hold the busy nodes until we're done destroying the old tree,
 936  * then copy the temporary tree over the (now emtpy) real tree.
 937  */
 938 void
 939 smbfs_destroy_table(struct vfs *vfsp)
 940 {
 941         avl_tree_t tmp_avl;
 942         smbmntinfo_t *mi;
 943         smbnode_t *np;
 944         smbnode_t *rlist;
 945         void *v;
 946 
 947         mi = VFTOSMI(vfsp);
 948         rlist = NULL;
 949         smbfs_init_hash_avl(&tmp_avl);


 994          */
 995         mi->smi_hash_avl = tmp_avl;
 996         rw_exit(&mi->smi_hash_lk);
 997 
 998         /*
 999          * Now destroy the nodes on our temporary list (rlist).
1000          * This call to smbfs_addfree will end up destroying the
1001          * smbnode, but in a safe way with the appropriate set
1002          * of checks done.
1003          */
1004         while ((np = rlist) != NULL) {
1005                 rlist = (smbnode_t *)np->r_avl_node.avl_child[0];
1006                 smbfs_addfree(np);
1007         }
1008 }
1009 
1010 /*
1011  * This routine destroys all the resources associated with the smbnode
1012  * and then the smbnode itself.  Note: sn_inactive has been called.
1013  *
1014  * From NFS: nfs_subr.c:destroy_rnode
1015  */
1016 static void
1017 sn_destroy_node(smbnode_t *np)
1018 {
1019         vnode_t *vp;
1020         vfs_t *vfsp;
1021 
1022         vp = SMBTOV(np);
1023         vfsp = vp->v_vfsp;
1024 
1025         ASSERT(vp->v_count == 1);
1026         ASSERT(np->r_count == 0);
1027         ASSERT(np->r_mapcnt == 0);
1028         ASSERT(np->r_secattr.vsa_aclentp == NULL);
1029         ASSERT(np->r_cred == NULL);
1030         ASSERT(np->n_rpath == NULL);
1031         ASSERT(!(np->r_flags & RHASHED));
1032         ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
1033         atomic_dec_ulong((ulong_t *)&smbnodenew);
1034         vn_invalid(vp);
1035         vn_free(vp);
1036         kmem_cache_free(smbnode_cache, np);
1037         VFS_RELE(vfsp);
1038 }
1039 
1040 /*
1041  * From NFS rflush()
1042  * Flush all vnodes in this (or every) vfs.
1043  * Used by smbfs_sync and by smbfs_unmount.
1044  */
1045 /*ARGSUSED*/
1046 void
1047 smbfs_rflush(struct vfs *vfsp, cred_t *cr)
1048 {
1049         smbmntinfo_t *mi;
1050         smbnode_t *np;
1051         vnode_t *vp, **vplist;
1052         long num, cnt;
1053 
1054         mi = VFTOSMI(vfsp);
1055 
1056         /*
1057          * Check to see whether there is anything to do.
1058          */
1059         num = avl_numnodes(&mi->smi_hash_avl);
1060         if (num == 0)
1061                 return;
1062 
1063         /*
1064          * Allocate a slot for all currently active rnodes on the
1065          * supposition that they all may need flushing.
1066          */
1067         vplist = kmem_alloc(num * sizeof (*vplist), KM_SLEEP);
1068         cnt = 0;
1069 
1070         /*
1071          * Walk the AVL tree looking for rnodes with page
1072          * lists associated with them.  Make a list of these
1073          * files.
1074          */
1075         rw_enter(&mi->smi_hash_lk, RW_READER);
1076         for (np = avl_first(&mi->smi_hash_avl); np != NULL;
1077             np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
1078                 vp = SMBTOV(np);
1079                 /*
1080                  * Don't bother sync'ing a vp if it
1081                  * is part of virtual swap device or
1082                  * if VFS is read-only
1083                  */
1084                 if (IS_SWAPVP(vp) || vn_is_readonly(vp))
1085                         continue;
1086                 /*
1087                  * If the vnode has pages and is marked as either
1088                  * dirty or mmap'd, hold and add this vnode to the
1089                  * list of vnodes to flush.
1090                  */
1091                 if (vn_has_cached_data(vp) &&
1092                     ((np->r_flags & RDIRTY) || np->r_mapcnt > 0)) {
1093                         VN_HOLD(vp);
1094                         vplist[cnt++] = vp;
1095                         if (cnt == num)
1096                                 break;
1097                 }
1098         }
1099         rw_exit(&mi->smi_hash_lk);
1100 
1101         /*
1102          * Flush and release all of the files on the list.
1103          */
1104         while (cnt-- > 0) {
1105                 vp = vplist[cnt];
1106                 (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr, NULL);
1107                 VN_RELE(vp);
1108         }
1109 
1110         kmem_free(vplist, num * sizeof (vnode_t *));
1111 }
1112 
1113 /* Here NFS has access cache stuff (nfs_subr.c) not used here */

1114 
1115 /*
1116  * Set or Clear direct I/O flag
1117  * VOP_RWLOCK() is held for write access to prevent a race condition
1118  * which would occur if a process is in the middle of a write when
1119  * directio flag gets set. It is possible that all pages may not get flushed.
1120  * From nfs_common.c
1121  */
1122 
1123 /* ARGSUSED */
1124 int
1125 smbfs_directio(vnode_t *vp, int cmd, cred_t *cr)
1126 {
1127         int     error = 0;
1128         smbnode_t       *np;
1129 
1130         np = VTOSMB(vp);
1131 
1132         if (cmd == DIRECTIO_ON) {
1133 
1134                 if (np->r_flags & RDIRECTIO)
1135                         return (0);
1136 
1137                 /*
1138                  * Flush the page cache.
1139                  */
1140 
1141                 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
1142 
1143                 if (np->r_flags & RDIRECTIO) {
1144                         VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1145                         return (0);
1146                 }
1147 
1148                 /* Here NFS also checks ->r_awcount */
1149                 if (vn_has_cached_data(vp) &&
1150                     (np->r_flags & RDIRTY) != 0) {
1151                         error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0,
1152                             B_INVAL, cr, NULL);
1153                         if (error) {
1154                                 if (error == ENOSPC || error == EDQUOT) {
1155                                         mutex_enter(&np->r_statelock);
1156                                         if (!np->r_error)
1157                                                 np->r_error = error;
1158                                         mutex_exit(&np->r_statelock);
1159                                 }
1160                                 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1161                                 return (error);
1162                         }
1163                 }
1164 
1165                 mutex_enter(&np->r_statelock);
1166                 np->r_flags |= RDIRECTIO;
1167                 mutex_exit(&np->r_statelock);
1168                 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1169                 return (0);
1170         }
1171 
1172         if (cmd == DIRECTIO_OFF) {
1173                 mutex_enter(&np->r_statelock);
1174                 np->r_flags &= ~RDIRECTIO;       /* disable direct mode */
1175                 mutex_exit(&np->r_statelock);
1176                 return (0);
1177         }
1178 
1179         return (EINVAL);
1180 }
1181 
1182 static kmutex_t smbfs_newnum_lock;
1183 static uint32_t smbfs_newnum_val = 0;
1184 
1185 /*
1186  * Return a number 0..0xffffffff that's different from the last
1187  * 0xffffffff numbers this returned.  Used for unlinked files.
1188  * From NFS nfs_subr.c newnum
1189  */
1190 uint32_t
1191 smbfs_newnum(void)
1192 {
1193         uint32_t id;
1194 
1195         mutex_enter(&smbfs_newnum_lock);
1196         if (smbfs_newnum_val == 0)
1197                 smbfs_newnum_val = (uint32_t)gethrestime_sec();
1198         id = smbfs_newnum_val++;
1199         mutex_exit(&smbfs_newnum_lock);
1200         return (id);
1201 }
1202 
1203 /*
1204  * Fill in a temporary name at buf
1205  */
1206 int
1207 smbfs_newname(char *buf, size_t buflen)
1208 {
1209         uint_t id;
1210         int n;
1211 
1212         id = smbfs_newnum();
1213         n = snprintf(buf, buflen, "~$smbfs%08X", id);
1214         return (n);
1215 }
1216 
1217 
1218 /*
1219  * initialize resources that are used by smbfs_subr.c
1220  * this is called from the _init() routine (by the way of smbfs_clntinit())
1221  *
1222  * From NFS: nfs_subr.c:nfs_subrinit
1223  */
1224 int
1225 smbfs_subrinit(void)
1226 {
1227         ulong_t nsmbnode_max;
1228 
1229         /*
1230          * Allocate and initialize the smbnode cache
1231          */
1232         if (nsmbnode <= 0)
1233                 nsmbnode = ncsize; /* dnlc.h */
1234         nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
1235             sizeof (struct smbnode));
1236         if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
1237                 cmn_err(CE_NOTE,
1238                     "setting nsmbnode to max value of %ld", nsmbnode_max);
1239                 nsmbnode = nsmbnode_max;
1240         }
1241 
1242         smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
1243             0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
1244 
1245         /*
1246          * Initialize the various mutexes and reader/writer locks
1247          */
1248         mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
1249         mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
1250 
1251         /*
1252          * Assign unique major number for all smbfs mounts
1253          */
1254         if ((smbfs_major = getudev()) == -1) {
1255                 cmn_err(CE_WARN,
1256                     "smbfs: init: can't get unique device number");
1257                 smbfs_major = 0;
1258         }
1259         smbfs_minor = 0;
1260 
1261         return (0);
1262 }
1263 
1264 /*
1265  * free smbfs hash table, etc.
1266  * From NFS: nfs_subr.c:nfs_subrfini
1267  */
1268 void
1269 smbfs_subrfini(void)
1270 {
1271 
1272         /*
1273          * Destroy the smbnode cache
1274          */
1275         kmem_cache_destroy(smbnode_cache);
1276 
1277         /*
1278          * Destroy the various mutexes and reader/writer locks
1279          */
1280         mutex_destroy(&smbfreelist_lock);
1281         mutex_destroy(&smbfs_minor_lock);
1282 }
1283 
1284 /* rddir_cache ? */
1285 
1286 /*


1321                  */
1322                 smbfs_addfree(np);
1323                 mutex_enter(&smbfreelist_lock);
1324         }
1325         mutex_exit(&smbfreelist_lock);
1326 }
1327 
1328 /*
1329  * Called by kmem_cache_alloc ask us if we could
1330  * "Please give back some memory!"
1331  *
1332  * Todo: dump nodes from the free list?
1333  */
1334 /*ARGSUSED*/
1335 void
1336 smbfs_kmem_reclaim(void *cdrarg)
1337 {
1338         smbfs_node_reclaim();
1339 }
1340 
1341 /*
1342  * Here NFS has failover stuff and
1343  * nfs_rw_xxx - see smbfs_rwlock.c
1344  */