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,34 **** --- 25,35 ---- * 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,51 **** --- 38,55 ---- #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,170 **** /* * Free the resources associated with an smbnode. * Note: This is different from smbfs_inactive * ! * NFS: nfs_subr.c:rinactive */ static void sn_inactive(smbnode_t *np) { vsecattr_t ovsa; cred_t *oldcr; char *orpath; int orplen; /* ! * Flush and invalidate all pages (todo) * Free any held credentials and caches... * etc. (See NFS code) */ mutex_enter(&np->r_statelock); --- 153,176 ---- /* * Free the resources associated with an smbnode. * Note: This is different from smbfs_inactive * ! * 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; /* ! * 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,189 **** --- 186,200 ---- 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,212 **** * 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 */ smbnode_t * smbfs_node_findcreate( smbmntinfo_t *mi, const char *dirnm, --- 213,223 ---- * 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. * ! * Based on NFS: nfs_subr.c:makenfsnode */ smbnode_t * smbfs_node_findcreate( smbmntinfo_t *mi, const char *dirnm,
*** 284,300 **** /* * 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. --- 295,304 ----
*** 303,319 **** */ return (np); } /* ! * NFS: nfs_subr.c:rtablehash * We use smbfs_hash(). */ /* * Find or create an smbnode. ! * NFS: nfs_subr.c:make_rnode */ static smbnode_t * make_smbnode( smbmntinfo_t *mi, const char *rpath, --- 307,323 ---- */ return (np); } /* ! * Here NFS has: nfs_subr.c:rtablehash * We use smbfs_hash(). */ /* * Find or create an smbnode. ! * From NFS: nfs_subr.c:make_rnode */ static smbnode_t * make_smbnode( smbmntinfo_t *mi, const char *rpath,
*** 427,449 **** /* 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_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 */ - 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); --- 431,449 ---- /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */ np->r_vnode = vp; np->n_mount = mi; ! np->n_fid = NULL; np->n_uid = mi->smi_uid; np->n_gid = mi->smi_gid; /* Leave attributes "stale." */ /* ! * Here NFS has avl_create(&np->r_dir, ...) ! * for the readdir cache (not used here). */ /* Now fill in the vnode. */ vn_setops(vp, smbfs_vnodeops); vp->v_data = (caddr_t)np; VFS_HOLD(vfsp);
*** 497,507 **** * 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 */ void smbfs_addfree(smbnode_t *np) { vnode_t *vp; --- 497,507 ---- * destroy immediately when we have too many smbnodes, etc. * * Normally called by smbfs_inactive, but also * called in here during cleanup operations. * ! * From NFS: nfs_subr.c:rp_addfree */ void smbfs_addfree(smbnode_t *np) { vnode_t *vp;
*** 625,635 **** * 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 */ static void sn_rmfree(smbnode_t *np) { --- 625,635 ---- * Remove an smbnode from the free list. * * The caller must be holding smbfreelist_lock and the smbnode * must be on the freelist. * ! * From NFS: nfs_subr.c:rp_rmfree */ static void sn_rmfree(smbnode_t *np) {
*** 651,696 **** /* * Put an smbnode in the "hash" AVL tree. * * The caller must be hold the rwlock as writer. * ! * 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); 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 */ 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); np->r_flags &= ~RHASHED; mutex_exit(&np->r_statelock); } /* * Remove an smbnode from the "hash" AVL tree. --- 651,696 ---- /* * Put an smbnode in the "hash" AVL tree. * * The caller must be hold the rwlock as writer. * ! * 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)); 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. * ! * 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)); 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,720 **** /* * Lookup an smbnode by remote pathname * * The caller must be holding the AVL rwlock, either shared or exclusive. * ! * NFS: nfs_subr.c:rfind */ static smbnode_t * sn_hashfind( smbmntinfo_t *mi, const char *rpath, --- 710,720 ---- /* * Lookup an smbnode by remote pathname * * The caller must be holding the AVL rwlock, either shared or exclusive. * ! * From NFS: nfs_subr.c:rfind */ static smbnode_t * sn_hashfind( smbmntinfo_t *mi, const char *rpath,
*** 865,875 **** * 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 * * Debugging changes here relative to NFS. * Relatively harmless, so left 'em in. */ int --- 865,875 ---- * 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. * ! * From NFS: nfs_subr.c:check_rtable * * Debugging changes here relative to NFS. * Relatively harmless, so left 'em in. */ int
*** 924,934 **** /* * 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 * * 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 --- 924,934 ---- /* * 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. * ! * 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,1019 **** /* * 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 */ static void sn_destroy_node(smbnode_t *np) { vnode_t *vp; --- 1009,1019 ---- /* * This routine destroys all the resources associated with the smbnode * and then the smbnode itself. Note: sn_inactive has been called. * ! * From NFS: nfs_subr.c:destroy_rnode */ static void sn_destroy_node(smbnode_t *np) { vnode_t *vp;
*** 1036,1063 **** kmem_cache_free(smbnode_cache, np); VFS_RELE(vfsp); } /* * Flush all vnodes in this (or every) vfs. ! * Used by nfs_sync and by nfs_unmount. */ /*ARGSUSED*/ void smbfs_rflush(struct vfs *vfsp, cred_t *cr) { ! /* Todo: mmap support. */ } ! /* access cache */ ! /* client handles */ /* * 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 */ int smbfs_subrinit(void) { ulong_t nsmbnode_max; --- 1036,1227 ---- kmem_cache_free(smbnode_cache, np); VFS_RELE(vfsp); } /* + * From NFS rflush() * Flush all vnodes in this (or every) vfs. ! * Used by smbfs_sync and by smbfs_unmount. */ /*ARGSUSED*/ void smbfs_rflush(struct vfs *vfsp, cred_t *cr) { ! 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 *)); } ! /* 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()) * ! * From NFS: nfs_subr.c:nfs_subrinit */ int smbfs_subrinit(void) { ulong_t nsmbnode_max;
*** 1068,1078 **** 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, "setting nsmbnode to max value of %ld", nsmbnode_max); nsmbnode = nsmbnode_max; } smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t), --- 1232,1242 ---- 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)) { ! 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,1096 **** /* * Assign unique major number for all smbfs mounts */ if ((smbfs_major = getudev()) == -1) { ! zcmn_err(GLOBAL_ZONEID, CE_WARN, "smbfs: init: can't get unique device number"); smbfs_major = 0; } smbfs_minor = 0; --- 1250,1260 ---- /* * Assign unique major number for all smbfs mounts */ if ((smbfs_major = getudev()) == -1) { ! cmn_err(CE_WARN, "smbfs: init: can't get unique device number"); smbfs_major = 0; } smbfs_minor = 0;
*** 1097,1107 **** return (0); } /* * free smbfs hash table, etc. ! * NFS: nfs_subr.c:nfs_subrfini */ void smbfs_subrfini(void) { --- 1261,1271 ---- return (0); } /* * free smbfs hash table, etc. ! * From NFS: nfs_subr.c:nfs_subrfini */ void smbfs_subrfini(void) {
*** 1172,1178 **** smbfs_kmem_reclaim(void *cdrarg) { smbfs_node_reclaim(); } ! /* nfs failover stuff */ ! /* nfs_rw_xxx - see smbfs_rwlock.c */ --- 1336,1344 ---- smbfs_kmem_reclaim(void *cdrarg) { smbfs_node_reclaim(); } ! /* ! * Here NFS has failover stuff and ! * nfs_rw_xxx - see smbfs_rwlock.c ! */