Print this page
    
NEX-19025 CIFS gets confused with filenames containing enhanced Unicode
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
and: (fix build, check-rtime)
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-2460 libfksmbd should not link with libsmb
re #6854 FindFirstFile,FindFirstFileEx,... are not working correctly on Nexenta CIFS-shares
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/common/smbsrv/smb_match.c
          +++ new/usr/src/common/smbsrv/smb_match.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  25   25   */
  26   26  
  27   27  #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
  28   28  #include <stdlib.h>
  29   29  #include <string.h>
  30   30  #else
  31   31  #include <sys/types.h>
  32   32  #include <sys/systm.h>
  33   33  #include <sys/sunddi.h>
  34   34  #endif
  35   35  #include <smbsrv/string.h>
  36   36  #include <smbsrv/smb.h>
  37   37  
  38   38  /*
  39   39   * Maximum recursion depth for the wildcard match functions.
  40   40   * These functions may recurse when processing a '*'.
  41   41   */
  42   42  #define SMB_MATCH_DEPTH_MAX     32
  43   43  
  44   44  struct match_priv {
  45   45          int depth;
  46   46          boolean_t ci;
  47   47  };
  48   48  
  49   49  static int smb_match_private(const char *, const char *, struct match_priv *);
  50   50  
  51   51  static const char smb_wildcards[] = "*?<>\"";
  52   52  
  53   53  /*
  54   54   * Return B_TRUE if pattern contains wildcards
  55   55   */
  56   56  boolean_t
  57   57  smb_contains_wildcards(const char *pattern)
  58   58  {
  59   59  
  60   60          return (strpbrk(pattern, smb_wildcards) != NULL);
  61   61  }
  62   62  
  63   63  /*
  64   64   * NT-compatible file name match function.  [MS-FSA 3.1.4.4]
  65   65   * Returns TRUE if there is a match.
  66   66   */
  67   67  boolean_t
  68   68  smb_match(const char *p, const char *s, boolean_t ci)
  69   69  {
  70   70          struct match_priv priv;
  71   71          int rc;
  72   72  
  73   73          /*
  74   74           * Optimize common patterns that match everything:
  75   75           * ("*", "<\"*")  That second one is the converted
  76   76           * form of "*.*" after smb_convert_wildcards() does
  77   77           * its work on it for an old LM client. Note that a
  78   78           * plain "*.*" never gets this far.
  79   79           */
  80   80          if (p[0] == '*' && p[1] == '\0')
  81   81                  return (B_TRUE);
  82   82          if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0')
  83   83                  return (B_TRUE);
  84   84  
  85   85          /*
  86   86           * Match string ".." as if "."  This is Windows behavior
  87   87           * (not mentioned in MS-FSA) that was determined using
  88   88           * the Samba masktest program.
  89   89           */
  90   90          if (s[0] == '.' && s[1] == '.' && s[2] == '\0')
  91   91                  s++;
  92   92  
  93   93          /*
  94   94           * Optimize simple patterns (no wildcards)
  95   95           */
  96   96          if (NULL == strpbrk(p, smb_wildcards)) {
  97   97                  if (ci)
  98   98                          rc = smb_strcasecmp(p, s, 0);
  99   99                  else
 100  100                          rc = strcmp(p, s);
 101  101                  return (rc == 0);
 102  102          }
 103  103  
 104  104          /*
 105  105           * Do real wildcard match.
 106  106           */
 107  107          priv.depth = 0;
 108  108          priv.ci = ci;
 109  109          rc = smb_match_private(p, s, &priv);
 110  110          return (rc == 1);
 111  111  }
 112  112  
 113  113  /*
 114  114   * Internal file name match function.  [MS-FSA 3.1.4.4]
 115  115   * This does the full expression evaluation.
 116  116   *
 117  117   * '*' matches zero of more of any characters.
 118  118   * '?' matches exactly one of any character.
 119  119   * '<' matches any string up through the last dot or EOS.
 120  120   * '>' matches any one char not a dot, dot at EOS, or EOS.
 121  121   * '"' matches a dot, or EOS.
 122  122   *
 123  123   * Returns:
 124  124   *  1   match
 125  125   *  0   no-match
 126  126   * -1   no-match, error (illseq, too many wildcards in pattern, ...)
 127  127   *
 128  128   * Note that both the pattern and the string are in multi-byte form.
 129  129   *
 130  130   * The implementation of this is quite tricky.  First note that it
 131  131   * can call itself recursively, though it limits the recursion depth.
 132  132   * Each switch case in the while loop can basically do one of three
 133  133   * things: (a) return "Yes, match", (b) return "not a match", or
 134  134   * continue processing the match pattern.  The cases for wildcards
  
    | 
      ↓ open down ↓ | 
    134 lines elided | 
    
      ↑ open up ↑ | 
  
 135  135   * that may match a variable number of characters ('*' and '<') do
 136  136   * recursive calls, looking for a match of the remaining pattern,
 137  137   * starting at the current and later positions in the string.
 138  138   */
 139  139  static int
 140  140  smb_match_private(const char *pat, const char *str, struct match_priv *priv)
 141  141  {
 142  142          const char      *limit;
 143  143          char            pc;             /* current pattern char */
 144  144          int             rc;
 145      -        smb_wchar_t     wcpat, wcstr;   /* current wchar in pat, str */
      145 +        uint32_t        wcpat, wcstr;   /* current wchar in pat, str */
 146  146          int             nbpat, nbstr;   /* multi-byte length of it */
 147  147  
 148  148          if (priv->depth >= SMB_MATCH_DEPTH_MAX)
 149  149                  return (-1);
 150  150  
 151  151          /*
 152  152           * Advance over one multi-byte char, used in cases like
 153  153           * '?' or '>' where "match one character" needs to be
 154  154           * interpreted as "match one multi-byte sequence".
 155  155           *
 156  156           * This macro needs to consume the semicolon following
 157  157           * each place it appears, so this is carefully written
 158  158           * as an if/else with a missing semicolon at the end.
 159  159           */
 160  160  #define ADVANCE(str) \
 161  161          if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \
 162  162                  return (-1); \
 163  163          else \
 164  164                  str += nbstr    /* no ; */
 165  165  
 166  166          /*
 167  167           * We move pat forward in each switch case so that the
 168  168           * default case can move it by a whole multi-byte seq.
 169  169           */
 170  170          while ((pc = *pat) != '\0') {
 171  171                  switch (pc) {
 172  172  
 173  173                  case '?':       /* exactly one of any character */
 174  174                          pat++;
 175  175                          if (*str != '\0') {
 176  176                                  ADVANCE(str);
 177  177                                  continue;
 178  178                          }
 179  179                          /* EOS: no-match */
 180  180                          return (0);
 181  181  
 182  182                  case '*':       /* zero or more of any characters */
 183  183                          pat++;
 184  184                          /* Optimize '*' at end of pattern. */
 185  185                          if (*pat == '\0')
 186  186                                  return (1); /* match */
 187  187                          while (*str != '\0') {
 188  188                                  priv->depth++;
 189  189                                  rc = smb_match_private(pat, str, priv);
 190  190                                  priv->depth--;
 191  191                                  if (rc != 0)
 192  192                                          return (rc); /* match */
 193  193                                  ADVANCE(str);
 194  194                          }
 195  195                          continue;
 196  196  
 197  197                  case '<':       /* any string up through the last dot or EOS */
 198  198                          pat++;
 199  199                          if ((limit = strrchr(str, '.')) != NULL)
 200  200                                  limit++;
 201  201                          while (*str != '\0' && str != limit) {
 202  202                                  priv->depth++;
 203  203                                  rc = smb_match_private(pat, str, priv);
 204  204                                  priv->depth--;
 205  205                                  if (rc != 0)
 206  206                                          return (rc); /* match */
 207  207                                  ADVANCE(str);
 208  208                          }
 209  209                          continue;
 210  210  
 211  211                  case '>':       /* anything not a dot, dot at EOS, or EOS */
 212  212                          pat++;
 213  213                          if (*str == '.') {
 214  214                                  if (str[1] == '\0') {
 215  215                                          /* dot at EOS */
 216  216                                          str++;  /* ADVANCE over '.' */
 217  217                                          continue;
 218  218                                  }
 219  219                                  /* dot NOT at EOS: no-match */
 220  220                                  return (0);
 221  221                          }
 222  222                          if (*str != '\0') {
 223  223                                  /* something not a dot */
 224  224                                  ADVANCE(str);
 225  225                                  continue;
 226  226                          }
 227  227                          continue;
 228  228  
 229  229                  case '\"':      /* dot, or EOS */
 230  230                          pat++;
 231  231                          if (*str == '.') {
 232  232                                  str++;  /* ADVANCE over '.' */
 233  233                                  continue;
 234  234                          }
 235  235                          if (*str == '\0') {
 236  236                                  continue;
 237  237                          }
 238  238                          /* something else: no-match */
 239  239                          return (0);
 240  240  
 241  241                  default:        /* not a wildcard */
 242  242                          nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX);
 243  243                          nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX);
 244  244                          /* make sure we advance */
 245  245                          if (nbpat < 1 || nbstr < 1)
 246  246                                  return (-1);
 247  247                          if (wcpat == wcstr) {
 248  248                                  pat += nbpat;
 249  249                                  str += nbstr;
 250  250                                  continue;
 251  251                          }
 252  252                          if (priv->ci) {
 253  253                                  wcpat = smb_tolower(wcpat);
 254  254                                  wcstr = smb_tolower(wcstr);
 255  255                                  if (wcpat == wcstr) {
 256  256                                          pat += nbpat;
 257  257                                          str += nbstr;
 258  258                                          continue;
 259  259                                  }
 260  260                          }
 261  261                          return (0); /* no-match */
 262  262                  }
 263  263          }
 264  264          return (*str == '\0');
 265  265  }
  
    | 
      ↓ open down ↓ | 
    110 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX