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>

@@ -25,10 +25,11 @@
  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  *      All rights reserved.
  */
 /*
  * Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * Node hash implementation initially borrowed from NFS (nfs_subr.c)
  * but then heavily modified. It's no longer an array of hash lists,

@@ -37,15 +38,18 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/time.h>
 #include <sys/vnode.h>
+#include <sys/atomic.h>
 #include <sys/bitmap.h>
+#include <sys/buf.h>
 #include <sys/dnlc.h>
 #include <sys/kmem.h>
 #include <sys/sunddi.h>
 #include <sys/sysmacros.h>
+#include <sys/fcntl.h>
 
 #include <netsmb/smb_osdep.h>
 
 #include <netsmb/smb.h>
 #include <netsmb/smb_conn.h>

@@ -149,22 +153,24 @@
 
 /*
  * Free the resources associated with an smbnode.
  * Note: This is different from smbfs_inactive
  *
- * NFS: nfs_subr.c:rinactive
+ * From NFS: nfs_subr.c:rinactive
  */
 static void
 sn_inactive(smbnode_t *np)
 {
         vsecattr_t      ovsa;
         cred_t          *oldcr;
         char            *orpath;
         int             orplen;
+        vnode_t         *vp;
 
         /*
-         * Flush and invalidate all pages (todo)
+         * Here NFS has:
+         * Flush and invalidate all pages (done by caller)
          * Free any held credentials and caches...
          * etc.  (See NFS code)
          */
         mutex_enter(&np->r_statelock);
 

@@ -180,10 +186,15 @@
         np->n_rpath = NULL;
         np->n_rplen = 0;
 
         mutex_exit(&np->r_statelock);
 
+        vp = SMBTOV(np);
+        if (vn_has_cached_data(vp)) {
+                ASSERT3P(vp,==,NULL);
+        }
+
         if (ovsa.vsa_aclentp != NULL)
                 kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
 
         if (oldcr != NULL)
                 crfree(oldcr);

@@ -202,11 +213,11 @@
  * Callers that need a node created but don't have the
  * real attributes pass smbfs_fattr0 to force creation.
  *
  * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
  *
- * NFS: nfs_subr.c:makenfsnode
+ * Based on NFS: nfs_subr.c:makenfsnode
  */
 smbnode_t *
 smbfs_node_findcreate(
         smbmntinfo_t *mi,
         const char *dirnm,

@@ -284,17 +295,10 @@
         /*
          * Apply the given attributes to this node,
          * dealing with any cache impact, etc.
          */
         vp = SMBTOV(np);
-        if (!newnode) {
-                /*
-                 * Found an existing node.
-                 * Maybe purge caches...
-                 */
-                smbfs_cache_check(vp, fap);
-        }
         smbfs_attrcache_fa(vp, fap);
 
         /*
          * Note NFS sets vp->v_type here, assuming it
          * can never change for the life of a node.

@@ -303,17 +307,17 @@
          */
         return (np);
 }
 
 /*
- * NFS: nfs_subr.c:rtablehash
+ * Here NFS has: nfs_subr.c:rtablehash
  * We use smbfs_hash().
  */
 
 /*
  * Find or create an smbnode.
- * NFS: nfs_subr.c:make_rnode
+ * From NFS: nfs_subr.c:make_rnode
  */
 static smbnode_t *
 make_smbnode(
         smbmntinfo_t *mi,
         const char *rpath,

@@ -427,23 +431,19 @@
         /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
 
         np->r_vnode = vp;
         np->n_mount = mi;
 
-        np->n_fid = SMB_FID_UNUSED;
+        np->n_fid = NULL;
         np->n_uid = mi->smi_uid;
         np->n_gid = mi->smi_gid;
         /* Leave attributes "stale." */
 
-#if 0 /* XXX dircache */
         /*
-         * We don't know if it's a directory yet.
-         * Let the caller do this?  XXX
+         * Here NFS has avl_create(&np->r_dir, ...)
+         * for the readdir cache (not used here).
          */
-        avl_create(&np->r_dir, compar, sizeof (rddir_cache),
-            offsetof(rddir_cache, tree));
-#endif
 
         /* Now fill in the vnode. */
         vn_setops(vp, smbfs_vnodeops);
         vp->v_data = (caddr_t)np;
         VFS_HOLD(vfsp);

@@ -497,11 +497,11 @@
  * destroy immediately when we have too many smbnodes, etc.
  *
  * Normally called by smbfs_inactive, but also
  * called in here during cleanup operations.
  *
- * NFS: nfs_subr.c:rp_addfree
+ * From NFS: nfs_subr.c:rp_addfree
  */
 void
 smbfs_addfree(smbnode_t *np)
 {
         vnode_t *vp;

@@ -625,11 +625,11 @@
  * Remove an smbnode from the free list.
  *
  * The caller must be holding smbfreelist_lock and the smbnode
  * must be on the freelist.
  *
- * NFS: nfs_subr.c:rp_rmfree
+ * From NFS: nfs_subr.c:rp_rmfree
  */
 static void
 sn_rmfree(smbnode_t *np)
 {
 

@@ -651,46 +651,46 @@
 /*
  * Put an smbnode in the "hash" AVL tree.
  *
  * The caller must be hold the rwlock as writer.
  *
- * NFS: nfs_subr.c:rp_addhash
+ * From NFS: nfs_subr.c:rp_addhash
  */
 static void
 sn_addhash_locked(smbnode_t *np, avl_index_t where)
 {
         smbmntinfo_t *mi = np->n_mount;
 
         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
-        ASSERT(!(np->r_flags & RHASHED));
 
-        avl_insert(&mi->smi_hash_avl, np, where);
-
         mutex_enter(&np->r_statelock);
+        if ((np->r_flags & RHASHED) == 0) {
+                avl_insert(&mi->smi_hash_avl, np, where);
         np->r_flags |= RHASHED;
+        }
         mutex_exit(&np->r_statelock);
 }
 
 /*
  * Remove an smbnode from the "hash" AVL tree.
  *
  * The caller must hold the rwlock as writer.
  *
- * NFS: nfs_subr.c:rp_rmhash_locked
+ * From NFS: nfs_subr.c:rp_rmhash_locked
  */
 static void
 sn_rmhash_locked(smbnode_t *np)
 {
         smbmntinfo_t *mi = np->n_mount;
 
         ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
-        ASSERT(np->r_flags & RHASHED);
 
-        avl_remove(&mi->smi_hash_avl, np);
-
         mutex_enter(&np->r_statelock);
+        if ((np->r_flags & RHASHED) != 0) {
         np->r_flags &= ~RHASHED;
+                avl_remove(&mi->smi_hash_avl, np);
+        }
         mutex_exit(&np->r_statelock);
 }
 
 /*
  * Remove an smbnode from the "hash" AVL tree.

@@ -710,11 +710,11 @@
 /*
  * Lookup an smbnode by remote pathname
  *
  * The caller must be holding the AVL rwlock, either shared or exclusive.
  *
- * NFS: nfs_subr.c:rfind
+ * From NFS: nfs_subr.c:rfind
  */
 static smbnode_t *
 sn_hashfind(
         smbmntinfo_t *mi,
         const char *rpath,

@@ -865,11 +865,11 @@
  * Several of these checks are done without holding the usual
  * locks.  This is safe because destroy_smbtable(), smbfs_addfree(),
  * etc. will redo the necessary checks before actually destroying
  * any smbnodes.
  *
- * NFS: nfs_subr.c:check_rtable
+ * From NFS: nfs_subr.c:check_rtable
  *
  * Debugging changes here relative to NFS.
  * Relatively harmless, so left 'em in.
  */
 int

@@ -924,11 +924,11 @@
 /*
  * Destroy inactive vnodes from the AVL tree which belong to this
  * vfs.  It is essential that we destroy all inactive vnodes during a
  * forced unmount as well as during a normal unmount.
  *
- * NFS: nfs_subr.c:destroy_rtable
+ * Based on NFS: nfs_subr.c:destroy_rtable
  *
  * In here, we're normally destrying all or most of the AVL tree,
  * so the natural choice is to use avl_destroy_nodes.  However,
  * there may be a few busy nodes that should remain in the AVL
  * tree when we're done.  The solution: use a temporary tree to

@@ -1009,11 +1009,11 @@
 
 /*
  * This routine destroys all the resources associated with the smbnode
  * and then the smbnode itself.  Note: sn_inactive has been called.
  *
- * NFS: nfs_subr.c:destroy_rnode
+ * From NFS: nfs_subr.c:destroy_rnode
  */
 static void
 sn_destroy_node(smbnode_t *np)
 {
         vnode_t *vp;

@@ -1036,28 +1036,192 @@
         kmem_cache_free(smbnode_cache, np);
         VFS_RELE(vfsp);
 }
 
 /*
+ * From NFS rflush()
  * Flush all vnodes in this (or every) vfs.
- * Used by nfs_sync and by nfs_unmount.
+ * Used by smbfs_sync and by smbfs_unmount.
  */
 /*ARGSUSED*/
 void
 smbfs_rflush(struct vfs *vfsp, cred_t *cr)
 {
-        /* Todo: mmap support. */
+        smbmntinfo_t *mi;
+        smbnode_t *np;
+        vnode_t *vp, **vplist;
+        long num, cnt;
+
+        mi = VFTOSMI(vfsp);
+
+        /*
+         * Check to see whether there is anything to do.
+         */
+        num = avl_numnodes(&mi->smi_hash_avl);
+        if (num == 0)
+                return;
+
+        /*
+         * Allocate a slot for all currently active rnodes on the
+         * supposition that they all may need flushing.
+         */
+        vplist = kmem_alloc(num * sizeof (*vplist), KM_SLEEP);
+        cnt = 0;
+
+        /*
+         * Walk the AVL tree looking for rnodes with page
+         * lists associated with them.  Make a list of these
+         * files.
+         */
+        rw_enter(&mi->smi_hash_lk, RW_READER);
+        for (np = avl_first(&mi->smi_hash_avl); np != NULL;
+            np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
+                vp = SMBTOV(np);
+                /*
+                 * Don't bother sync'ing a vp if it
+                 * is part of virtual swap device or
+                 * if VFS is read-only
+                 */
+                if (IS_SWAPVP(vp) || vn_is_readonly(vp))
+                        continue;
+                /*
+                 * If the vnode has pages and is marked as either
+                 * dirty or mmap'd, hold and add this vnode to the
+                 * list of vnodes to flush.
+                 */
+                if (vn_has_cached_data(vp) &&
+                    ((np->r_flags & RDIRTY) || np->r_mapcnt > 0)) {
+                        VN_HOLD(vp);
+                        vplist[cnt++] = vp;
+                        if (cnt == num)
+                                break;
+                }
+        }
+        rw_exit(&mi->smi_hash_lk);
+
+        /*
+         * Flush and release all of the files on the list.
+         */
+        while (cnt-- > 0) {
+                vp = vplist[cnt];
+                (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr, NULL);
+                VN_RELE(vp);
+        }
+
+        kmem_free(vplist, num * sizeof (vnode_t *));
 }
 
-/* access cache */
-/* client handles */
+/* Here NFS has access cache stuff (nfs_subr.c) not used here */
 
 /*
+ * Set or Clear direct I/O flag
+ * VOP_RWLOCK() is held for write access to prevent a race condition
+ * which would occur if a process is in the middle of a write when
+ * directio flag gets set. It is possible that all pages may not get flushed.
+ * From nfs_common.c
+ */
+
+/* ARGSUSED */
+int
+smbfs_directio(vnode_t *vp, int cmd, cred_t *cr)
+{
+        int     error = 0;
+        smbnode_t       *np;
+
+        np = VTOSMB(vp);
+
+        if (cmd == DIRECTIO_ON) {
+
+                if (np->r_flags & RDIRECTIO)
+                        return (0);
+
+                /*
+                 * Flush the page cache.
+                 */
+
+                (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
+
+                if (np->r_flags & RDIRECTIO) {
+                        VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
+                        return (0);
+                }
+
+                /* Here NFS also checks ->r_awcount */
+                if (vn_has_cached_data(vp) &&
+                    (np->r_flags & RDIRTY) != 0) {
+                        error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0,
+                            B_INVAL, cr, NULL);
+                        if (error) {
+                                if (error == ENOSPC || error == EDQUOT) {
+                                        mutex_enter(&np->r_statelock);
+                                        if (!np->r_error)
+                                                np->r_error = error;
+                                        mutex_exit(&np->r_statelock);
+                                }
+                                VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
+                                return (error);
+                        }
+                }
+
+                mutex_enter(&np->r_statelock);
+                np->r_flags |= RDIRECTIO;
+                mutex_exit(&np->r_statelock);
+                VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
+                return (0);
+        }
+
+        if (cmd == DIRECTIO_OFF) {
+                mutex_enter(&np->r_statelock);
+                np->r_flags &= ~RDIRECTIO;      /* disable direct mode */
+                mutex_exit(&np->r_statelock);
+                return (0);
+        }
+
+        return (EINVAL);
+}
+
+static kmutex_t smbfs_newnum_lock;
+static uint32_t smbfs_newnum_val = 0;
+
+/*
+ * Return a number 0..0xffffffff that's different from the last
+ * 0xffffffff numbers this returned.  Used for unlinked files.
+ * From NFS nfs_subr.c newnum
+ */
+uint32_t
+smbfs_newnum(void)
+{
+        uint32_t id;
+
+        mutex_enter(&smbfs_newnum_lock);
+        if (smbfs_newnum_val == 0)
+                smbfs_newnum_val = (uint32_t)gethrestime_sec();
+        id = smbfs_newnum_val++;
+        mutex_exit(&smbfs_newnum_lock);
+        return (id);
+}
+
+/*
+ * Fill in a temporary name at buf
+ */
+int
+smbfs_newname(char *buf, size_t buflen)
+{
+        uint_t id;
+        int n;
+
+        id = smbfs_newnum();
+        n = snprintf(buf, buflen, "~$smbfs%08X", id);
+        return (n);
+}
+
+
+/*
  * initialize resources that are used by smbfs_subr.c
  * this is called from the _init() routine (by the way of smbfs_clntinit())
  *
- * NFS: nfs_subr.c:nfs_subrinit
+ * From NFS: nfs_subr.c:nfs_subrinit
  */
 int
 smbfs_subrinit(void)
 {
         ulong_t nsmbnode_max;

@@ -1068,11 +1232,11 @@
         if (nsmbnode <= 0)
                 nsmbnode = ncsize; /* dnlc.h */
         nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
             sizeof (struct smbnode));
         if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
-                zcmn_err(GLOBAL_ZONEID, CE_NOTE,
+                cmn_err(CE_NOTE,
                     "setting nsmbnode to max value of %ld", nsmbnode_max);
                 nsmbnode = nsmbnode_max;
         }
 
         smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),

@@ -1086,11 +1250,11 @@
 
         /*
          * Assign unique major number for all smbfs mounts
          */
         if ((smbfs_major = getudev()) == -1) {
-                zcmn_err(GLOBAL_ZONEID, CE_WARN,
+                cmn_err(CE_WARN,
                     "smbfs: init: can't get unique device number");
                 smbfs_major = 0;
         }
         smbfs_minor = 0;
 

@@ -1097,11 +1261,11 @@
         return (0);
 }
 
 /*
  * free smbfs hash table, etc.
- * NFS: nfs_subr.c:nfs_subrfini
+ * From NFS: nfs_subr.c:nfs_subrfini
  */
 void
 smbfs_subrfini(void)
 {
 

@@ -1172,7 +1336,9 @@
 smbfs_kmem_reclaim(void *cdrarg)
 {
         smbfs_node_reclaim();
 }
 
-/* nfs failover stuff */
-/* nfs_rw_xxx - see smbfs_rwlock.c */
+/*
+ * Here NFS has failover stuff and
+ * nfs_rw_xxx - see smbfs_rwlock.c
+ */