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