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)
        
*** 31,43 ****
   *
   * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $
   */
  
  /*
-  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/time.h>
--- 31,44 ----
   *
   * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $
   */
  
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
+  *
+  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/time.h>
*** 45,54 ****
--- 46,56 ----
  #include <sys/sunddi.h>
  
  #include <netsmb/smb_osdep.h>
  
  #include <netsmb/smb.h>
+ #include <netsmb/smb2.h>
  #include <netsmb/smb_conn.h>
  #include <netsmb/smb_subr.h>
  #include <netsmb/smb_rq.h>
  
  #include <smbfs/smbfs.h>
*** 71,84 ****
  {
          int caseopt = SMB_CS_NONE;
          int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0;
          int error;
  
!         if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0)
!                 caseopt |= SMB_CS_UPPER;
! 
!         if (unicode) {
                  error = mb_put_padbyte(mbp);
                  if (error)
                          return (error);
          }
  
--- 73,87 ----
  {
          int caseopt = SMB_CS_NONE;
          int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0;
          int error;
  
!         /*
!          * SMB1 may need an alignment pad before (not SMB2)
!          */
!         if (((vcp)->vc_flags & SMBV_SMB2) == 0 &&
!             ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) {
                  error = mb_put_padbyte(mbp);
                  if (error)
                          return (error);
          }
  
*** 120,134 ****
                  error = smb_put_dmem(mbp, vcp,
                      name, nmlen, caseopt, NULL);
                  if (error)
                          return (error);
          }
!         /* Put NULL termination. */
          if (unicode)
                  error = mb_put_uint16le(mbp, 0);
          else
                  error = mb_put_uint8(mbp, 0);
  
          return (error);
  }
  
  /*
--- 123,140 ----
                  error = smb_put_dmem(mbp, vcp,
                      name, nmlen, caseopt, NULL);
                  if (error)
                          return (error);
          }
! 
!         /* SMB1 wants NULL termination. */
!         if (((vcp)->vc_flags & SMBV_SMB2) == 0) {
                  if (unicode)
                          error = mb_put_uint16le(mbp, 0);
                  else
                          error = mb_put_uint8(mbp, 0);
+         }
  
          return (error);
  }
  
  /*
*** 180,185 ****
--- 186,459 ----
           * Conversion failed, but our caller does not
           * deal with errors here, so just put a "?".
           * Don't expect to ever see this.
           */
          (void) strlcpy(ctx->f_name, "?", ctx->f_namesz);
+ }
+ 
+ /*
+  * Decode a directory entry from OtW form into ctx->f_attr
+  *
+  * Caller already put some (wire-format) directory entries
+  * into ctx->f_mdchain and we expect to find one.
+  *
+  * Advancing correctly through the buffer can be tricky if one
+  * tries to add up the size of an entry as you go (which is how
+  * the darwin code this is derived from did it).  The easiest way
+  * to correctly advance the position is to get a whole dirent
+  * into another mdchain (entry_mdc) based on NextEntryOffset,
+  * and then scan the data from that mdchain.  On the last entry,
+  * we don't know the entire length, so just scan directly from
+  * what remains of the multi-entry buffer instead of trying to
+  * figure out the length to copy into a separate mdchain.
+  */
+ int
+ smbfs_decode_dirent(struct smbfs_fctx *ctx)
+ {
+         struct mdchain entry_mdc;
+         struct mdchain *mdp = &ctx->f_mdchain;
+         size_t nmlen;
+         uint64_t llongint;
+         uint32_t nmsize, dattr;
+         uint32_t nextoff = 0;
+         int error;
+ 
+         /* In case we error out... */
+         ctx->f_nmlen = 0;
+         ctx->f_rkey = (uint32_t)-1;
+         bzero(&entry_mdc, sizeof (entry_mdc));
+ 
+         /*
+          * Setup mdp to point to an mbchain holding
+          * what should be a single directory entry.
+          */
+         error = md_get_uint32le(mdp, &nextoff);
+         if (error != 0)
+                 goto errout;
+         if (nextoff >= 4) {
+                 /*
+                  * More entries follow.  Make a new mbchain
+                  * holding just this one entry, then advance.
+                  */
+                 mblk_t *m = NULL;
+                 error = md_get_mbuf(mdp, nextoff - 4, &m);
+                 if (error != 0)
+                         goto errout;
+                 md_initm(&entry_mdc, m);
+                 mdp = &entry_mdc;
+                 ctx->f_eofs += nextoff;
+         } else {
+                 /* Scan directly from ctx->f_mdchain */
+                 ctx->f_eofs = ctx->f_left;
+         }
+ 
+         /*
+          * Decode the fixed-size parts
+          */
+         switch (ctx->f_infolevel) {
+         case FileFullDirectoryInformation:
+         case SMB_FIND_FULL_DIRECTORY_INFO:
+                 md_get_uint32le(mdp, &ctx->f_rkey);     /* resume key (idx) */
+                 md_get_uint64le(mdp, &llongint);        /* creation time */
+                 smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime);
+                 md_get_uint64le(mdp, &llongint);
+                 smb_time_NT2local(llongint, &ctx->f_attr.fa_atime);
+                 md_get_uint64le(mdp, &llongint);
+                 smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime);
+                 md_get_uint64le(mdp, &llongint);
+                 smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime);
+                 md_get_uint64le(mdp, &llongint);        /* file size */
+                 ctx->f_attr.fa_size = llongint;
+                 md_get_uint64le(mdp, &llongint);        /* alloc. size */
+                 ctx->f_attr.fa_allocsz = llongint;
+                 md_get_uint32le(mdp, &dattr);   /* ext. file attributes */
+                 ctx->f_attr.fa_attr = dattr;
+                 error = md_get_uint32le(mdp, &nmsize);  /* name size (otw) */
+                 if (error)
+                         goto errout;
+                 md_get_uint32le(mdp, NULL);     /* Ea size */
+                 break;
+ 
+         case FileStreamInformation:
+                 error = md_get_uint32le(mdp, &nmsize);  /* name size (otw) */
+                 md_get_uint64le(mdp, &llongint);        /* file size */
+                 ctx->f_attr.fa_size = llongint;
+                 md_get_uint64le(mdp, &llongint);        /* alloc. size */
+                 ctx->f_attr.fa_allocsz = llongint;
+                 /*
+                  * Stream names start with a ':' that we want to skip.
+                  * This is the easiest place to take care of that.
+                  * Always unicode here.
+                  */
+                 if (nmsize >= 2) {
+                         struct mdchain save_mdc;
+                         uint16_t wch;
+                         save_mdc = *mdp;
+                         md_get_uint16le(mdp, &wch);
+                         if (wch == ':') {
+                                 /* OK, we skipped the ':' */
+                                 nmsize -= 2;
+                         } else {
+                                 SMBVDEBUG("No leading : in stream?\n");
+                                 /* restore position */
+                                 *mdp = save_mdc;
+                         }
+                 }
+                 break;
+ 
+         default:
+                 SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel);
+                 error = EINVAL;
+                 goto errout;
+         }
+ 
+         /*
+          * Get the filename, and convert to utf-8
+          * Allocated f_name in findopen
+          */
+         nmlen = ctx->f_namesz;
+         error = smb_get_dstring(mdp, SSTOVC(ctx->f_ssp),
+             ctx->f_name, &nmlen, nmsize);
+         if (error != 0)
+                 goto errout;
+         ctx->f_nmlen = (int)nmlen;
+         md_done(&entry_mdc);
+         return (0);
+ 
+ errout:
+         /*
+          * Something bad has happened and we ran out of data
+          * before we could parse all f_ecnt entries expected.
+          * Give up on the current buffer.
+          */
+         SMBVDEBUG("ran out of data\n");
+         ctx->f_eofs = ctx->f_left;
+         md_done(&entry_mdc);
+         return (error);
+ }
+ 
+ /*
+  * Decode FileAllInformation
+  *
+  * The data is a concatenation of:
+  *      FileBasicInformation
+  *      FileStandardInformation
+  *      FileInternalInformation
+  *      FileEaInformation
+  *      FilePositionInformation
+  *      FileModeInformation
+  *      FileAlignmentInformation
+  *      FileNameInformation
+  */
+ /*ARGSUSED*/
+ int
+ smbfs_decode_file_all_info(struct smb_share *ssp,
+         struct mdchain *mdp, struct smbfattr *fap)
+ {
+         uint64_t llongint, lsize;
+         uint32_t dattr;
+         int error;
+ 
+         /*
+          * This part is: FileBasicInformation
+          */
+ 
+         /* creation time */
+         md_get_uint64le(mdp, &llongint);
+         smb_time_NT2local(llongint, &fap->fa_createtime);
+ 
+         /* last access time */
+         md_get_uint64le(mdp, &llongint);
+         smb_time_NT2local(llongint, &fap->fa_atime);
+ 
+         /* last write time */
+         md_get_uint64le(mdp, &llongint);
+         smb_time_NT2local(llongint, &fap->fa_mtime);
+ 
+         /* last change time */
+         md_get_uint64le(mdp, &llongint);
+         smb_time_NT2local(llongint, &fap->fa_ctime);
+ 
+         /* attributes */
+         md_get_uint32le(mdp, &dattr);
+         fap->fa_attr = dattr;
+ 
+         /* reserved */
+         md_get_uint32le(mdp, NULL);
+ 
+         /*
+          * This part is: FileStandardInformation
+          */
+ 
+         /* allocation size */
+         md_get_uint64le(mdp, &lsize);
+         fap->fa_allocsz = lsize;
+ 
+         /* File size */
+         error = md_get_uint64le(mdp, &lsize);
+         fap->fa_size = lsize;
+ 
+         /*
+          * There's more after this but we don't need it:
+          * Remainder of FileStandardInformation
+          *      NumLlinks, DeletOnClose, IsDir, reserved.
+          * Then:
+          *      FileInternalInformation
+          *      FileEaInformation
+          *      FilePositionInformation
+          *      FileModeInformation
+          *      FileAlignmentInformation
+          *      FileNameInformation
+          */
+ 
+         return (error);
+ }
+ 
+ /*
+  * Decode FileFsAttributeInformation
+  *
+  *    ULONG FileSystemAttributes;
+  *    LONG MaximumComponentNameLength;
+  *    ULONG FileSystemNameLength;
+  *    WCHAR FileSystemName[1];
+  */
+ int
+ smbfs_decode_fs_attr_info(struct smb_share *ssp,
+         struct mdchain *mdp, struct smb_fs_attr_info *fsa)
+ {
+         struct smb_vc *vcp = SSTOVC(ssp);
+         uint32_t nlen;
+         int error;
+ 
+         md_get_uint32le(mdp, &fsa->fsa_aflags);
+         md_get_uint32le(mdp, &fsa->fsa_maxname);
+         error = md_get_uint32le(mdp, &nlen);    /* fs name length */
+         if (error)
+                 goto out;
+ 
+         /*
+          * Get the FS type name.
+          */
+         bzero(fsa->fsa_tname, FSTYPSZ);
+         if (SMB_UNICODE_STRINGS(vcp)) {
+                 uint16_t tmpbuf[FSTYPSZ];
+                 size_t tmplen, outlen;
+ 
+                 if (nlen > sizeof (tmpbuf))
+                         nlen = sizeof (tmpbuf);
+                 error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
+                 if (error != 0)
+                         goto out;
+                 tmplen = nlen / 2;      /* UCS-2 chars */
+                 outlen = FSTYPSZ - 1;
+                 error = uconv_u16tou8(tmpbuf, &tmplen,
+                     (uchar_t *)fsa->fsa_tname, &outlen,
+                     UCONV_IN_LITTLE_ENDIAN);
+         } else {
+                 if (nlen > (FSTYPSZ - 1))
+                         nlen = FSTYPSZ - 1;
+                 error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
+         }
+ 
+ out:
+         return (error);
  }