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