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-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
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>

*** 22,31 **** --- 22,33 ---- /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. * All rights reserved. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/thread.h>
*** 58,79 **** --- 60,87 ---- #include <smbfs/smbfs.h> #include <smbfs/smbfs_node.h> #include <smbfs/smbfs_subr.h> + #ifdef _KERNEL #include <vm/hat.h> #include <vm/as.h> #include <vm/page.h> #include <vm/pvn.h> #include <vm/seg.h> #include <vm/seg_map.h> #include <vm/seg_vn.h> + #endif // _KERNEL + #define ATTRCACHE_VALID(vp) (gethrtime() < VTOSMB(vp)->r_attrtime) + static int smbfs_getattr_cache(vnode_t *, smbfattr_t *); static void smbfattr_to_vattr(vnode_t *, smbfattr_t *, vattr_t *); static void smbfattr_to_xvattr(smbfattr_t *, vattr_t *); + static int smbfs_getattr_otw(vnode_t *, struct smbfattr *, cred_t *); + /* * The following code provide zone support in order to perform an action * for each smbfs mount in a zone. This is also where we would add * per-zone globals and kernel threads for the smbfs module (since * they must be terminated by the shutdown callback).
*** 100,157 **** * that have not changed for a long time. There are minimum and maximum * timeout values that can be set per mount point. */ /* ! * Validate caches by checking cached attributes. If they have timed out ! * get the attributes from the server and compare mtimes. If mtimes are ! * different purge all caches for this vnode. */ int smbfs_validate_caches( struct vnode *vp, cred_t *cr) { ! struct vattr va; ! va.va_mask = AT_SIZE; ! return (smbfsgetattr(vp, &va, cr)); } /* * Purge all of the various data caches. */ ! /*ARGSUSED*/ void ! smbfs_purge_caches(struct vnode *vp) { ! #if 0 /* not yet: mmap support */ /* ! * NFS: Purge the DNLC for this vp, * Clear any readdir state bits, * the readlink response cache, ... */ - smbnode_t *np = VTOSMB(vp); /* * Flush the page cache. */ if (vn_has_cached_data(vp)) { (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL); } ! #endif /* not yet */ } /* * Check the attribute cache to see if the new attributes match * those cached. If they do, the various `data' caches are * considered to be good. Otherwise, purge the cached data. */ ! void smbfs_cache_check( struct vnode *vp, ! struct smbfattr *fap) { smbnode_t *np; int purge_data = 0; int purge_acl = 0; --- 108,218 ---- * that have not changed for a long time. There are minimum and maximum * timeout values that can be set per mount point. */ /* ! * Helper for _validate_caches */ int + smbfs_waitfor_purge_complete(vnode_t *vp) + { + smbnode_t *np; + k_sigset_t smask; + + np = VTOSMB(vp); + if (np->r_serial != NULL && np->r_serial != curthread) { + mutex_enter(&np->r_statelock); + sigintr(&smask, VTOSMI(vp)->smi_flags & SMI_INT); + while (np->r_serial != NULL) { + if (!cv_wait_sig(&np->r_cv, &np->r_statelock)) { + sigunintr(&smask); + mutex_exit(&np->r_statelock); + return (EINTR); + } + } + sigunintr(&smask); + mutex_exit(&np->r_statelock); + } + return (0); + } + + /* + * Validate caches by checking cached attributes. If the cached + * attributes have timed out, then get new attributes from the server. + * As a side affect, this will do cache invalidation if the attributes + * have changed. + * + * If the attributes have not timed out and if there is a cache + * invalidation being done by some other thread, then wait until that + * thread has completed the cache invalidation. + */ + int smbfs_validate_caches( struct vnode *vp, cred_t *cr) { ! struct smbfattr fa; ! int error; ! if (ATTRCACHE_VALID(vp)) { ! error = smbfs_waitfor_purge_complete(vp); ! if (error) ! return (error); ! return (0); ! } ! ! return (smbfs_getattr_otw(vp, &fa, cr)); } /* * Purge all of the various data caches. + * + * Here NFS also had a flags arg to control what gets flushed. + * We only have the page cache, so no flags arg. */ ! /* ARGSUSED */ void ! smbfs_purge_caches(struct vnode *vp, cred_t *cr) { ! /* ! * Here NFS has: Purge the DNLC for this vp, * Clear any readdir state bits, * the readlink response cache, ... */ /* * Flush the page cache. */ if (vn_has_cached_data(vp)) { (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL); } ! ! /* ! * Here NFS has: Flush the readdir response cache. ! * No readdir cache in smbfs. ! */ } /* + * Here NFS has: + * nfs_purge_rddir_cache() + * nfs3_cache_post_op_attr() + * nfs3_cache_post_op_vattr() + * nfs3_cache_wcc_data() + */ + + /* * Check the attribute cache to see if the new attributes match * those cached. If they do, the various `data' caches are * considered to be good. Otherwise, purge the cached data. */ ! static void smbfs_cache_check( struct vnode *vp, ! struct smbfattr *fap, ! cred_t *cr) { smbnode_t *np; int purge_data = 0; int purge_acl = 0;
*** 172,212 **** if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec || np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec) purge_acl = 1; if (purge_acl) { - /* just invalidate r_secattr (XXX: OK?) */ np->r_sectime = gethrtime(); } mutex_exit(&np->r_statelock); if (purge_data) ! smbfs_purge_caches(vp); } /* - * Set attributes cache for given vnode using vnode attributes. - * From NFS: nfs_attrcache_va - */ - #if 0 /* not yet (not sure if we need this) */ - void - smbfs_attrcache_va(vnode_t *vp, struct vattr *vap) - { - smbfattr_t fa; - smbnode_t *np; - - vattr_to_fattr(vp, vap, &fa); - smbfs_attrcache_fa(vp, &fa); - } - #endif /* not yet */ - - /* * Set attributes cache for given vnode using SMB fattr * and update the attribute cache timeout. * ! * From NFS: nfs_attrcache, nfs_attrcache_va */ void smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap) { smbnode_t *np; --- 233,256 ---- if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec || np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec) purge_acl = 1; if (purge_acl) { np->r_sectime = gethrtime(); } mutex_exit(&np->r_statelock); if (purge_data) ! smbfs_purge_caches(vp, cr); } /* * Set attributes cache for given vnode using SMB fattr * and update the attribute cache timeout. * ! * Based on NFS: nfs_attrcache, nfs_attrcache_va */ void smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap) { smbnode_t *np;
*** 291,312 **** */ newsize = fap->fa_size; if (vtype == VDIR && newsize < DEV_BSIZE) newsize = DEV_BSIZE; ! if (np->r_size != newsize) { ! #if 0 /* not yet: mmap support */ ! if (!vn_has_cached_data(vp) || ...) ! /* XXX: See NFS page cache code. */ ! #endif /* not yet */ /* OK to set the size. */ np->r_size = newsize; } ! /* NFS: np->r_flags &= ~RWRITEATTR; */ ! np->n_flag &= ~NATTRCHANGED; mutex_exit(&np->r_statelock); if (oldvt != vtype) { SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype); } --- 335,359 ---- */ newsize = fap->fa_size; if (vtype == VDIR && newsize < DEV_BSIZE) newsize = DEV_BSIZE; ! if (np->r_size != newsize && ! (!vn_has_cached_data(vp) || ! (!(np->r_flags & RDIRTY) && np->r_count == 0))) { /* OK to set the size. */ np->r_size = newsize; } ! /* ! * Here NFS has: ! * nfs_setswaplike(vp, va); ! * np->r_flags &= ~RWRITEATTR; ! * (not needed here) ! */ + np->n_flag &= ~NATTRCHANGED; mutex_exit(&np->r_statelock); if (oldvt != vtype) { SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype); }
*** 346,366 **** * Get attributes over-the-wire and update attributes cache * if no error occurred in the over-the-wire operation. * Return 0 if successful, otherwise error. * From NFS: nfs_getattr_otw */ ! int smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) { - struct smbnode *np; struct smb_cred scred; int error; ! np = VTOSMB(vp); /* ! * NFS uses the ACL rpc here (if smi_flags & SMI_ACL) * With SMB, getting the ACL is a significantly more * expensive operation, so we do that only when asked * for the uid/gid. See smbfsgetattr(). */ --- 393,425 ---- * Get attributes over-the-wire and update attributes cache * if no error occurred in the over-the-wire operation. * Return 0 if successful, otherwise error. * From NFS: nfs_getattr_otw */ ! static int smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) { struct smb_cred scred; + smbnode_t *np = VTOSMB(vp); + smb_share_t *ssp = np->n_mount->smi_share; + smb_fh_t *fhp = NULL; int error; ! bzero(fap, sizeof (*fap)); /* ! * Special case the XATTR directory here (all fake). ! * OK to leave a,c,m times zero (expected). ! */ ! if (vp->v_flag & V_XATTRDIR) { ! fap->fa_attr = SMB_FA_DIR; ! fap->fa_size = DEV_BSIZE; ! return (0); ! } ! ! /* ! * Here NFS uses the ACL RPC (if smi_flags & SMI_ACL) * With SMB, getting the ACL is a significantly more * expensive operation, so we do that only when asked * for the uid/gid. See smbfsgetattr(). */
*** 367,384 **** /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); smb_credinit(&scred, cr); ! bzero(fap, sizeof (*fap)); ! error = smbfs_smb_getfattr(np, fap, &scred); smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); if (error) { ! /* NFS had: PURGE_STALE_FH(error, vp, cr) */ smbfs_attrcache_remove(np); if (error == ENOENT || error == ENOTDIR) { /* * Getattr failed because the object was * removed or renamed by another client. --- 426,463 ---- /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); smb_credinit(&scred, cr); ! // Does the attr. open code path work for streams? ! // Trying that, and if it doesn't work enable this. ! #if 0 // XXX ! /* ! * Extended attribute files ! */ ! if (np->n_flag & N_XATTR) { ! error = smbfs_xa_getfattr(np, fap, scrp); ! goto out; ! } ! #endif // XXX + if (np->n_fidrefs > 0 && + (fhp = np->n_fid) != NULL && + (fhp->fh_vcgenid == ssp->ss_vcgenid)) { + /* Use the FID we have. */ + error = smbfs_smb_getfattr(np, fhp, fap, &scred); + + } else { + /* This will do an attr open */ + error = smbfs_smb_getpattr(np, fap, &scred); + } + smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); if (error) { ! /* Here NFS has: PURGE_STALE_FH(error, vp, cr) */ smbfs_attrcache_remove(np); if (error == ENOENT || error == ENOTDIR) { /* * Getattr failed because the object was * removed or renamed by another client.
*** 388,410 **** } return (error); } /* ! * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr); * which did: fattr_to_vattr, nfs_attr_cache. * We cache the fattr form, so just do the * cache check and store the attributes. */ ! smbfs_cache_check(vp, fap); smbfs_attrcache_fa(vp, fap); return (0); } /* ! * Return either cached or remote attributes. If get remote attr * use them to check and invalidate caches, then cache the new attributes. * * From NFS: nfsgetattr() */ int --- 467,489 ---- } return (error); } /* ! * Here NFS has: nfs_cache_fattr(vap, fa, vap, t, cr); * which did: fattr_to_vattr, nfs_attr_cache. * We cache the fattr form, so just do the * cache check and store the attributes. */ ! smbfs_cache_check(vp, fap, cr); smbfs_attrcache_fa(vp, fap); return (0); } /* ! * Return either cached or remote attributes. If we get remote attrs, * use them to check and invalidate caches, then cache the new attributes. * * From NFS: nfsgetattr() */ int
*** 549,558 **** --- 628,706 ---- XVA_SET_RTN(xvap, XAT_HIDDEN); } } /* + * Here NFS has: + * nfs_async_... stuff + * which we're not using (no async I/O), and: + * writerp(), + * nfs_putpages() + * nfs_invalidate_pages() + * which we have in smbfs_vnops.c, and + * nfs_printfhandle() + * nfs_write_error() + * not needed here. + */ + + /* + * Helper function for smbfs_sync + * + * Walk the per-zone list of smbfs mounts, calling smbfs_rflush + * on each one. This is a little tricky because we need to exit + * the list mutex before each _rflush call and then try to resume + * where we were in the list after re-entering the mutex. + */ + void + smbfs_flushall(cred_t *cr) + { + smi_globals_t *smg; + smbmntinfo_t *tmp_smi, *cur_smi, *next_smi; + + smg = zone_getspecific(smi_list_key, crgetzone(cr)); + ASSERT(smg != NULL); + + mutex_enter(&smg->smg_lock); + cur_smi = list_head(&smg->smg_list); + if (cur_smi == NULL) { + mutex_exit(&smg->smg_lock); + return; + } + VFS_HOLD(cur_smi->smi_vfsp); + mutex_exit(&smg->smg_lock); + + flush: + smbfs_rflush(cur_smi->smi_vfsp, cr); + + mutex_enter(&smg->smg_lock); + /* + * Resume after cur_smi if that's still on the list, + * otherwise restart at the head. + */ + for (tmp_smi = list_head(&smg->smg_list); + tmp_smi != NULL; + tmp_smi = list_next(&smg->smg_list, tmp_smi)) + if (tmp_smi == cur_smi) + break; + if (tmp_smi != NULL) + next_smi = list_next(&smg->smg_list, tmp_smi); + else + next_smi = list_head(&smg->smg_list); + + if (next_smi != NULL) + VFS_HOLD(next_smi->smi_vfsp); + VFS_RELE(cur_smi->smi_vfsp); + + mutex_exit(&smg->smg_lock); + + if (next_smi != NULL) { + cur_smi = next_smi; + goto flush; + } + } + + /* * SMB Client initialization and cleanup. * Much of it is per-zone now. */
*** 702,714 **** /* no-op */ } smb_fscb_t smbfs_cb = { .fscb_disconn = smbfs_dead, ! .fscb_connect = smbfs_cb_nop, ! .fscb_down = smbfs_cb_nop, ! .fscb_up = smbfs_cb_nop }; #endif /* NEED_SMBFS_CALLBACKS */ /* * SMBFS Client initialization routine. This routine should only be called --- 850,861 ---- /* no-op */ } smb_fscb_t smbfs_cb = { .fscb_disconn = smbfs_dead, ! .fscb_connect = smbfs_cb_nop ! }; #endif /* NEED_SMBFS_CALLBACKS */ /* * SMBFS Client initialization routine. This routine should only be called