Print this page
    
NEX-2807 Restoring previous versions from snapshots doesn't work with nested folders.
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-4538 SMB1 create file should support extended_response format (2)
NEX-6116 Failures in smbtorture raw.open
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Include this commit if upstreaming/backporting any of:
NEX-4540 SMB server declines EA support incorrectly
NEX-4239 smbtorture create failures re. allocation size
(illumos) 6398 SMB should support path names longer than 1024
NEX-5082 panic getting Mac attributes for a 255 character file name
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Conflicts:
        usr/src/uts/common/fs/smbsrv/smb_pathname.c
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
SMB-115 Support SMB path names with length > 1024
SMB-100 Internal error if filename is too long
Approved by: Gordon Ross <gwr@nexenta.com>
SMB-136 Snapshots not visible in Windows previous versions
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-65 SMB server in non-global zones (use zone_kcred())
re #13470 rb4432 Sync some SMB differences from illumos
re #6854 FindFirstFile,FindFirstFileEx,... are not working correctly on Nexenta CIFS-shares
re #6813 rb1757 port 2976 Child folder visibility through shares
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/smbsrv/smb_pathname.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb_pathname.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   *
  
    | 
      ↓ open down ↓ | 
    12 lines elided | 
    
      ↑ open up ↑ | 
  
  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   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23      - * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
       23 + * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
  24   24   */
  25   25  
  26   26  #include <smbsrv/smb_kproto.h>
  27   27  #include <smbsrv/smb_fsops.h>
  28   28  #include <sys/pathname.h>
  29   29  #include <sys/sdt.h>
  30   30  
  31   31  static char *smb_pathname_catia_v5tov4(smb_request_t *, char *, char *, int);
  32   32  static char *smb_pathname_catia_v4tov5(smb_request_t *, char *, char *, int);
  33   33  static int smb_pathname_lookup(pathname_t *, pathname_t *, int,
  34   34      vnode_t **, vnode_t *, vnode_t *, smb_attr_t *attr, cred_t *);
  35   35  static char *smb_pathname_strdup(smb_request_t *, const char *);
  36   36  static char *smb_pathname_strcat(smb_request_t *, char *, const char *);
  37   37  static void smb_pathname_preprocess(smb_request_t *, smb_pathname_t *);
  38   38  static void smb_pathname_preprocess_quota(smb_request_t *, smb_pathname_t *);
  39   39  static int smb_pathname_dfs_preprocess(smb_request_t *, char *, size_t);
  40   40  static void smb_pathname_preprocess_adminshare(smb_request_t *,
  41   41      smb_pathname_t *);
  42   42  
  43   43  
  44   44  uint32_t
  45   45  smb_is_executable(char *path)
  46   46  {
  47   47          char    extension[5];
  48   48          int     len = strlen(path);
  49   49  
  50   50          if ((len >= 4) && (path[len - 4] == '.')) {
  51   51                  (void) strcpy(extension, &path[len - 3]);
  52   52                  (void) smb_strupr(extension);
  53   53  
  54   54                  if (strcmp(extension, "EXE") == 0)
  55   55                          return (NODE_FLAGS_EXECUTABLE);
  56   56  
  57   57                  if (strcmp(extension, "COM") == 0)
  58   58                          return (NODE_FLAGS_EXECUTABLE);
  59   59  
  60   60                  if (strcmp(extension, "DLL") == 0)
  61   61                          return (NODE_FLAGS_EXECUTABLE);
  62   62  
  63   63                  if (strcmp(extension, "SYM") == 0)
  64   64                          return (NODE_FLAGS_EXECUTABLE);
  65   65          }
  66   66  
  67   67          return (0);
  68   68  }
  69   69  
  70   70  /*
  71   71   * smb_pathname_reduce
  72   72   *
  73   73   * smb_pathname_reduce() takes a path and returns the smb_node for the
  74   74   * second-to-last component of the path.  It also returns the name of the last
  75   75   * component.  Pointers for both of these fields must be supplied by the caller.
  76   76   *
  77   77   * Upon success, 0 is returned.
  78   78   *
  79   79   * Upon error, *dir_node will be set to 0.
  80   80   *
  81   81   * *sr (in)
  82   82   * ---
  83   83   * smb_request structure pointer
  84   84   *
  85   85   * *cred (in)
  86   86   * -----
  87   87   * credential
  88   88   *
  89   89   * *path (in)
  90   90   * -----
  91   91   * pathname to be looked up
  92   92   *
  93   93   * *share_root_node (in)
  94   94   * ----------------
  95   95   * File operations which are share-relative should pass sr->tid_tree->t_snode.
  96   96   * If the call is not for a share-relative operation, this parameter must be 0
  97   97   * (e.g. the call from smbsr_setup_share()).  (Such callers will have path
  98   98   * operations done using root_smb_node.)  This parameter is used to determine
  99   99   * whether mount points can be crossed.
 100  100   *
 101  101   * share_root_node should have at least one reference on it.  This reference
 102  102   * will stay intact throughout this routine.
 103  103   *
 104  104   * *cur_node (in)
 105  105   * ---------
 106  106   * The smb_node for the current directory (for relative paths).
 107  107   * cur_node should have at least one reference on it.
 108  108   * This reference will stay intact throughout this routine.
 109  109   *
 110  110   * **dir_node (out)
 111  111   * ----------
 112  112   * Directory for the penultimate component of the original path.
 113  113   * (Note that this is not the same as the parent directory of the ultimate
 114  114   * target in the case of a link.)
 115  115   *
 116  116   * The directory smb_node is returned held.  The caller will need to release
 117  117   * the hold or otherwise make sure it will get released (e.g. in a destroy
 118  118   * routine if made part of a global structure).
 119  119   *
 120  120   * last_component (out)
 121  121   * --------------
 122  122   * The last component of the path.  (This may be different from the name of any
 123  123   * link target to which the last component may resolve.)
 124  124   *
 125  125   *
 126  126   * ____________________________
 127  127   *
 128  128   * The CIFS server lookup path needs to have logic equivalent to that of
 129  129   * smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the
 130  130   * following areas:
 131  131   *
 132  132   *      - traversal of child mounts (handled by smb_pathname_reduce)
 133  133   *      - unmangling                (handled in smb_pathname)
 134  134   *      - "chroot" behavior of share root (handled by lookuppnvp)
 135  135   *
 136  136   * In addition, it needs to replace backslashes with forward slashes.  It also
 137  137   * ensures that link processing is done correctly, and that directory
 138  138   * information requested by the caller is correctly returned (i.e. for paths
 139  139   * with a link in the last component, the directory information of the
 140  140   * link and not the target needs to be returned).
 141  141   */
 142  142  
 143  143  int
  
    | 
      ↓ open down ↓ | 
    110 lines elided | 
    
      ↑ open up ↑ | 
  
 144  144  smb_pathname_reduce(
 145  145      smb_request_t       *sr,
 146  146      cred_t              *cred,
 147  147      const char          *path,
 148  148      smb_node_t          *share_root_node,
 149  149      smb_node_t          *cur_node,
 150  150      smb_node_t          **dir_node,
 151  151      char                *last_component)
 152  152  {
 153  153          smb_node_t      *root_node;
 154      -        pathname_t      ppn;
      154 +        pathname_t      ppn, mnt_pn;
 155  155          char            *usepath;
 156  156          int             lookup_flags = FOLLOW;
 157  157          int             trailing_slash = 0;
 158  158          int             err = 0;
 159  159          int             len;
 160      -        smb_node_t      *vss_cur_node;
 161      -        smb_node_t      *vss_root_node;
      160 +        smb_node_t      *vss_node;
 162  161          smb_node_t      *local_cur_node;
 163  162          smb_node_t      *local_root_node;
      163 +        boolean_t       chk_vss;
      164 +        char            *gmttoken;
 164  165  
 165  166          ASSERT(dir_node);
 166  167          ASSERT(last_component);
 167  168  
 168  169          *dir_node = NULL;
 169  170          *last_component = '\0';
 170      -        vss_cur_node = NULL;
 171      -        vss_root_node = NULL;
      171 +        vss_node = NULL;
      172 +        gmttoken = NULL;
      173 +        chk_vss = B_FALSE;
 172  174  
 173  175          if (sr && sr->tid_tree) {
 174  176                  if (STYPE_ISIPC(sr->tid_tree->t_res_type))
 175  177                          return (EACCES);
 176  178          }
 177  179  
 178  180          if (SMB_TREE_IS_CASEINSENSITIVE(sr))
 179  181                  lookup_flags |= FIGNORECASE;
 180  182  
 181  183          if (path == NULL)
 182  184                  return (EINVAL);
 183  185  
 184  186          if (*path == '\0')
 185  187                  return (ENOENT);
 186  188  
 187  189          usepath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
 188  190  
 189  191          len = strlcpy(usepath, path, SMB_MAXPATHLEN);
 190  192          if (len >= SMB_MAXPATHLEN) {
 191  193                  kmem_free(usepath, SMB_MAXPATHLEN);
 192  194                  return (ENAMETOOLONG);
 193  195          }
 194  196  
 195  197          (void) strsubst(usepath, '\\', '/');
 196  198  
 197  199          if (share_root_node)
 198  200                  root_node = share_root_node;
 199  201          else
 200  202                  root_node = sr->sr_server->si_root_smb_node;
 201  203  
 202  204          if (cur_node == NULL)
 203  205                  cur_node = root_node;
 204  206  
 205  207          local_cur_node = cur_node;
 206  208          local_root_node = root_node;
 207  209  
 208  210          if (SMB_TREE_IS_DFSROOT(sr)) {
 209  211                  int is_dfs;
 210  212                  if (sr->session->dialect >= SMB_VERS_2_BASE)
 211  213                          is_dfs = sr->smb2_hdr_flags &
 212  214                              SMB2_FLAGS_DFS_OPERATIONS;
 213  215                  else
 214  216                          is_dfs = sr->smb_flg2 & SMB_FLAGS2_DFS;
 215  217                  if (is_dfs != 0) {
 216  218                          err = smb_pathname_dfs_preprocess(sr, usepath,
  
    | 
      ↓ open down ↓ | 
    35 lines elided | 
    
      ↑ open up ↑ | 
  
 217  219                              SMB_MAXPATHLEN);
 218  220                          if (err != 0) {
 219  221                                  kmem_free(usepath, SMB_MAXPATHLEN);
 220  222                                  return (err);
 221  223                          }
 222  224                          len = strlen(usepath);
 223  225                  }
 224  226          }
 225  227  
 226  228          if (sr != NULL) {
 227      -                boolean_t chk_vss;
 228      -                if (sr->session->dialect >= SMB_VERS_2_BASE)
      229 +                if (sr->session->dialect >= SMB_VERS_2_BASE) {
 229  230                          chk_vss = sr->arg.open.create_timewarp;
 230      -                else
      231 +                } else {
 231  232                          chk_vss = (sr->smb_flg2 &
 232  233                              SMB_FLAGS2_REPARSE_PATH) != 0;
 233      -                if (chk_vss) {
 234      -                        err = smb_vss_lookup_nodes(sr, root_node, cur_node,
 235      -                            usepath, &vss_cur_node, &vss_root_node);
 236      -                        if (err != 0) {
 237      -                                kmem_free(usepath, SMB_MAXPATHLEN);
 238      -                                return (err);
 239      -                        }
 240  234  
 241      -                        len = strlen(usepath);
 242      -                        local_cur_node = vss_cur_node;
 243      -                        local_root_node = vss_root_node;
      235 +                        if (chk_vss) {
      236 +                                gmttoken = kmem_alloc(SMB_VSS_GMT_SIZE,
      237 +                                    KM_SLEEP);
      238 +                                err = smb_vss_extract_gmttoken(usepath,
      239 +                                    gmttoken);
      240 +                                if (err != 0) {
      241 +                                        kmem_free(usepath, SMB_MAXPATHLEN);
      242 +                                        kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
      243 +                                        return (err);
      244 +                                }
      245 +                                len = strlen(usepath);
      246 +                        }
 244  247                  }
      248 +                if (chk_vss)
      249 +                        (void) pn_alloc(&mnt_pn);
 245  250          }
 246  251  
 247  252          if (usepath[len - 1] == '/')
 248  253                  trailing_slash = 1;
 249  254  
 250  255          (void) strcanon(usepath, "/");
 251  256  
 252  257          (void) pn_alloc_sz(&ppn, SMB_MAXPATHLEN);
 253  258  
 254  259          if ((err = pn_set(&ppn, usepath)) != 0) {
 255  260                  (void) pn_free(&ppn);
 256  261                  kmem_free(usepath, SMB_MAXPATHLEN);
 257      -                if (vss_cur_node != NULL)
 258      -                        (void) smb_node_release(vss_cur_node);
 259      -                if (vss_root_node != NULL)
 260      -                        (void) smb_node_release(vss_root_node);
      262 +                if (chk_vss)
      263 +                        (void) pn_free(&mnt_pn);
      264 +                if (gmttoken != NULL)
      265 +                        kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
 261  266                  return (err);
 262  267          }
 263  268  
 264  269          /*
 265  270           * If a path does not have a trailing slash, strip off the
 266  271           * last component.  (We only need to return an smb_node for
 267  272           * the second to last component; a name is returned for the
 268  273           * last component.)
      274 +         *
      275 +         * For VSS requests, the last component might be a filesystem of its
      276 +         * own, and we need to discover that before exiting this function,
      277 +         * so allow the lookup to happen on the last component.
      278 +         * We'll correct this later when we convert to the snapshot.
 269  279           */
 270  280  
 271      -        if (trailing_slash) {
 272      -                (void) strlcpy(last_component, ".", MAXNAMELEN);
 273      -        } else {
 274      -                (void) pn_setlast(&ppn);
 275      -                (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN);
 276      -                ppn.pn_path[0] = '\0';
      281 +        if (!chk_vss) {
      282 +                if (trailing_slash) {
      283 +                        (void) strlcpy(last_component, ".", MAXNAMELEN);
      284 +                } else {
      285 +                        (void) pn_setlast(&ppn);
      286 +                        (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN);
      287 +                        ppn.pn_path[0] = '\0';
      288 +                }
 277  289          }
 278  290  
 279  291          if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) {
 280  292                  smb_node_ref(local_cur_node);
 281  293                  *dir_node = local_cur_node;
 282  294          } else {
 283  295                  err = smb_pathname(sr, ppn.pn_buf, lookup_flags,
 284      -                    local_root_node, local_cur_node, NULL, dir_node, cred);
      296 +                    local_root_node, local_cur_node, NULL, dir_node, cred,
      297 +                    chk_vss ? &mnt_pn : NULL);
 285  298          }
 286  299  
 287  300          (void) pn_free(&ppn);
 288  301          kmem_free(usepath, SMB_MAXPATHLEN);
 289  302  
 290  303          /*
      304 +         * We need to try and convert to snapshots, even on error.
      305 +         * This is to handle the following cases:
      306 +         * - We're on the lowest level filesystem, but a directory got renamed
      307 +         *   on the live version. We'll get ENOENT, but can still find it in
      308 +         *   the snapshot.
      309 +         * - The last component was actually a file. We need to leave the last
      310 +         *   component in in case it is, itself, a mountpoint, but that means
      311 +         *   we might get ENOTDIR if it's not actually a directory.
      312 +         *
      313 +         * Note that if you change the share-relative name of a mountpoint,
      314 +         * you won't be able to access previous versions of files under it.
      315 +         */
      316 +        if (chk_vss && *dir_node != NULL) {
      317 +                if ((err = smb_vss_lookup_nodes(sr, *dir_node, &vss_node,
      318 +                    gmttoken)) == 0) {
      319 +                        char *p = mnt_pn.pn_path;
      320 +                        size_t pathleft;
      321 +
      322 +                        smb_node_release(*dir_node);
      323 +                        *dir_node = NULL;
      324 +                        pathleft = pn_pathleft(&mnt_pn);
      325 +
      326 +                        if (pathleft == 0 || trailing_slash) {
      327 +                                (void) strlcpy(last_component, ".", MAXNAMELEN);
      328 +                        } else {
      329 +                                (void) pn_setlast(&mnt_pn);
      330 +                                (void) strlcpy(last_component, mnt_pn.pn_path,
      331 +                                    MAXNAMELEN);
      332 +                                mnt_pn.pn_path[0] = '\0';
      333 +                                pathleft -= strlen(last_component);
      334 +                        }
      335 +
      336 +                        if (pathleft != 0) {
      337 +                                err = smb_pathname(sr, p, lookup_flags,
      338 +                                    vss_node, vss_node, NULL, dir_node, cred,
      339 +                                    NULL);
      340 +                        } else {
      341 +                                *dir_node = vss_node;
      342 +                                vss_node = NULL;
      343 +                        }
      344 +                }
      345 +        }
      346 +
      347 +        if (chk_vss)
      348 +                (void) pn_free(&mnt_pn);
      349 +        if (gmttoken != NULL)
      350 +                kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
      351 +
      352 +        /*
 291  353           * Prevent traversal to another file system if mount point
 292  354           * traversal is disabled.
 293  355           *
 294  356           * Note that we disregard whether the traversal of the path went
 295  357           * outside of the file system and then came back (say via a link).
 296  358           * This means that only symlinks that are expressed relatively to
 297  359           * the share root work.
 298  360           *
 299  361           * share_root_node is NULL when mapping a share, so we disregard
 300  362           * that case.
 301  363           */
 302  364  
 303  365          if ((err == 0) && share_root_node) {
 304  366                  if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) {
 305  367                          err = EACCES;
 306  368                          if ((sr) && (sr)->tid_tree &&
 307  369                              smb_tree_has_feature((sr)->tid_tree,
 308  370                              SMB_TREE_TRAVERSE_MOUNTS))
 309  371                                  err = 0;
 310  372                  }
  
    | 
      ↓ open down ↓ | 
    10 lines elided | 
    
      ↑ open up ↑ | 
  
 311  373          }
 312  374  
 313  375          if (err) {
 314  376                  if (*dir_node) {
 315  377                          (void) smb_node_release(*dir_node);
 316  378                          *dir_node = NULL;
 317  379                  }
 318  380                  *last_component = 0;
 319  381          }
 320  382  
 321      -        if (vss_cur_node != NULL)
 322      -                (void) smb_node_release(vss_cur_node);
 323      -        if (vss_root_node != NULL)
 324      -                (void) smb_node_release(vss_root_node);
 325      -
      383 +        if (vss_node != NULL)
      384 +                (void) smb_node_release(vss_node);
 326  385          return (err);
 327  386  }
 328  387  
 329  388  /*
 330  389   * smb_pathname()
 331  390   * wrapper to lookuppnvp().  Handles name unmangling.
 332  391   *
 333  392   * *dir_node is the true directory of the target *node.
 334  393   *
 335  394   * If any component but the last in the path is not found, ENOTDIR instead of
 336  395   * ENOENT will be returned.
 337  396   *
 338  397   * Path components are processed one at a time so that smb_nodes can be
 339  398   * created for each component.  This allows the n_dnode field in the
 340  399   * smb_node to be properly populated.
 341  400   *
 342  401   * Because of the above, links are also processed in this routine
 343  402   * (i.e., we do not pass the FOLLOW flag to lookuppnvp()).  This
 344  403   * will allow smb_nodes to be created for each component of a link.
 345  404   *
 346  405   * Mangle checking is per component. If a name is mangled, when the
 347  406   * unmangled name is passed to smb_pathname_lookup() do not pass
 348  407   * FIGNORECASE, since the unmangled name is the real on-disk name.
 349  408   * Otherwise pass FIGNORECASE if it's set in flags. This will cause the
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
 350  409   * file system to return "first match" in the event of a case collision.
 351  410   *
 352  411   * If CATIA character translation is enabled it is applied to each
 353  412   * component before passing the component to smb_pathname_lookup().
 354  413   * After smb_pathname_lookup() the reverse translation is applied.
 355  414   */
 356  415  
 357  416  int
 358  417  smb_pathname(smb_request_t *sr, char *path, int flags,
 359  418      smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node,
 360      -    smb_node_t **ret_node, cred_t *cred)
      419 +    smb_node_t **ret_node, cred_t *cred, pathname_t *mnt_pn)
 361  420  {
 362  421          char            *component, *real_name, *namep;
 363  422          pathname_t      pn, rpn, upn, link_pn;
 364      -        smb_node_t      *dnode, *fnode;
      423 +        smb_node_t      *dnode, *fnode, *mnt_node;
 365  424          smb_attr_t      attr;
 366  425          vnode_t         *rootvp, *vp;
 367  426          size_t          pathleft;
 368  427          int             err = 0;
 369  428          int             nlink = 0;
 370  429          int             local_flags;
 371  430          uint32_t        abe_flag = 0;
 372  431          char            namebuf[MAXNAMELEN];
      432 +        vnode_t *fsrootvp = NULL;
 373  433  
 374  434          if (path == NULL)
 375  435                  return (EINVAL);
 376  436  
 377  437          ASSERT(root_node);
 378  438          ASSERT(cur_node);
 379  439          ASSERT(ret_node);
 380  440  
 381  441          *ret_node = NULL;
 382  442  
 383  443          if (dir_node)
 384  444                  *dir_node = NULL;
 385  445  
 386  446          (void) pn_alloc_sz(&upn, SMB_MAXPATHLEN);
 387  447  
 388  448          if ((err = pn_set(&upn, path)) != 0) {
 389  449                  (void) pn_free(&upn);
 390  450                  return (err);
 391  451          }
 392  452  
      453 +        if (mnt_pn != NULL && (err = pn_set(mnt_pn, path) != 0)) {
      454 +                (void) pn_free(&upn);
      455 +                return (err);
      456 +        }
      457 +
 393  458          if (SMB_TREE_SUPPORTS_ABE(sr))
 394  459                  abe_flag = SMB_ABE;
 395  460  
 396  461          (void) pn_alloc(&pn);
 397  462          (void) pn_alloc(&rpn);
 398  463  
 399  464          component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
 400  465          real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
 401  466  
      467 +        if (mnt_pn != NULL) {
      468 +                mnt_node = cur_node;
      469 +                smb_node_ref(cur_node);
      470 +        } else
      471 +                mnt_node = NULL;
 402  472          fnode = NULL;
 403  473          dnode = cur_node;
 404  474          smb_node_ref(dnode);
 405  475          rootvp = root_node->vp;
 406  476  
 407  477          while ((pathleft = pn_pathleft(&upn)) != 0) {
 408  478                  if (fnode) {
 409  479                          smb_node_release(dnode);
 410  480                          dnode = fnode;
 411  481                          fnode = NULL;
 412  482                  }
 413  483  
 414  484                  if ((err = pn_getcomponent(&upn, component)) != 0)
 415  485                          break;
  
    | 
      ↓ open down ↓ | 
    4 lines elided | 
    
      ↑ open up ↑ | 
  
 416  486  
 417  487                  if ((namep = smb_pathname_catia_v5tov4(sr, component,
 418  488                      namebuf, sizeof (namebuf))) == NULL) {
 419  489                          err = EILSEQ;
 420  490                          break;
 421  491                  }
 422  492  
 423  493                  if ((err = pn_set(&pn, namep)) != 0)
 424  494                          break;
 425  495  
      496 +                /* We want the DOS attributes. */
      497 +                bzero(&attr, sizeof (attr));
      498 +                attr.sa_mask = SMB_AT_DOSATTR;
      499 +
 426  500                  local_flags = flags & FIGNORECASE;
 427  501                  err = smb_pathname_lookup(&pn, &rpn, local_flags,
 428  502                      &vp, rootvp, dnode->vp, &attr, cred);
 429  503  
 430  504                  if (err) {
 431  505                          if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
 432  506                              !smb_maybe_mangled(component))
 433  507                                  break;
 434  508  
 435  509                          if ((err = smb_unmangle(dnode, component,
 436  510                              real_name, MAXNAMELEN, abe_flag)) != 0)
 437  511                                  break;
 438  512  
 439  513                          if ((namep = smb_pathname_catia_v5tov4(sr, real_name,
 440  514                              namebuf, sizeof (namebuf))) == NULL) {
 441  515                                  err = EILSEQ;
 442  516                                  break;
 443  517                          }
 444  518  
 445  519                          if ((err = pn_set(&pn, namep)) != 0)
 446  520                                  break;
 447  521  
 448  522                          local_flags = 0;
 449  523                          err = smb_pathname_lookup(&pn, &rpn, local_flags,
 450  524                              &vp, rootvp, dnode->vp, &attr, cred);
 451  525                          if (err)
 452  526                                  break;
 453  527                  }
 454  528  
 455  529                  /*
 456  530                   * This check MUST be done before symlink check
 457  531                   * since a reparse point is of type VLNK but should
 458  532                   * not be handled like a regular symlink.
 459  533                   */
 460  534                  if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
 461  535                          err = EREMOTE;
 462  536                          VN_RELE(vp);
 463  537                          break;
 464  538                  }
 465  539  
 466  540                  if ((vp->v_type == VLNK) &&
 467  541                      ((flags & FOLLOW) || pn_pathleft(&upn))) {
 468  542  
 469  543                          if (++nlink > MAXSYMLINKS) {
 470  544                                  err = ELOOP;
 471  545                                  VN_RELE(vp);
 472  546                                  break;
 473  547                          }
 474  548  
 475  549                          (void) pn_alloc(&link_pn);
 476  550                          err = pn_getsymlink(vp, &link_pn, cred);
 477  551                          VN_RELE(vp);
 478  552  
 479  553                          if (err == 0) {
 480  554                                  if (pn_pathleft(&link_pn) == 0)
 481  555                                          (void) pn_set(&link_pn, ".");
 482  556                                  err = pn_insert(&upn, &link_pn,
 483  557                                      strlen(component));
 484  558                          }
 485  559                          pn_free(&link_pn);
 486  560  
 487  561                          if (err)
 488  562                                  break;
 489  563  
 490  564                          if (upn.pn_pathlen == 0) {
 491  565                                  err = ENOENT;
 492  566                                  break;
 493  567                          }
 494  568  
 495  569                          if (upn.pn_path[0] == '/') {
 496  570                                  fnode = root_node;
 497  571                                  smb_node_ref(fnode);
 498  572                          }
 499  573  
 500  574                          if (pn_fixslash(&upn))
 501  575                                  flags |= FOLLOW;
 502  576  
 503  577                  } else {
 504  578                          if (flags & FIGNORECASE) {
 505  579                                  if (strcmp(rpn.pn_path, "/") != 0)
 506  580                                          pn_setlast(&rpn);
 507  581                                  namep = rpn.pn_path;
 508  582                          } else {
 509  583                                  namep = pn.pn_path;
 510  584                          }
 511  585  
 512  586                          namep = smb_pathname_catia_v4tov5(sr, namep,
 513  587                              namebuf, sizeof (namebuf));
 514  588  
 515  589                          fnode = smb_node_lookup(sr, NULL, cred, vp, namep,
 516  590                              dnode, NULL);
 517  591                          VN_RELE(vp);
 518  592  
 519  593                          if (fnode == NULL) {
  
    | 
      ↓ open down ↓ | 
    84 lines elided | 
    
      ↑ open up ↑ | 
  
 520  594                                  err = ENOMEM;
 521  595                                  break;
 522  596                          }
 523  597                  }
 524  598  
 525  599                  while (upn.pn_path[0] == '/') {
 526  600                          upn.pn_path++;
 527  601                          upn.pn_pathlen--;
 528  602                  }
 529  603  
      604 +                /*
      605 +                 * If the node we looked up is the root of a filesystem,
      606 +                 * snapshot the lookup so we can replay this after discovering
      607 +                 * the lowest mounted filesystem.
      608 +                 */
      609 +                if (mnt_pn != NULL &&
      610 +                    (err = VFS_ROOT(fnode->vp->v_vfsp, &fsrootvp)) == 0) {
      611 +                        if (fsrootvp == fnode->vp) {
      612 +                                mnt_pn->pn_pathlen = pn_pathleft(&upn);
      613 +                                mnt_pn->pn_path = mnt_pn->pn_buf +
      614 +                                    ((ptrdiff_t)upn.pn_path -
      615 +                                    (ptrdiff_t)upn.pn_buf);
      616 +
      617 +                                smb_node_ref(fnode);
      618 +                                if (mnt_node != NULL)
      619 +                                        smb_node_release(mnt_node);
      620 +                                mnt_node = fnode;
      621 +
      622 +                        }
      623 +                        VN_RELE(fsrootvp);
      624 +                }
 530  625          }
 531  626  
 532  627          if ((pathleft) && (err == ENOENT))
 533  628                  err = ENOTDIR;
 534  629  
 535      -        if (err) {
      630 +        if (mnt_node == NULL)
      631 +                mnt_pn = NULL;
      632 +
      633 +        /*
      634 +         * We always want to return a node when we're doing VSS
      635 +         * (mnt_pn != NULL)
      636 +         */
      637 +        if (mnt_pn == NULL && err != 0) {
 536  638                  if (fnode)
 537  639                          smb_node_release(fnode);
 538  640                  if (dnode)
 539  641                          smb_node_release(dnode);
 540  642          } else {
 541      -                *ret_node = fnode;
      643 +                if (mnt_pn != NULL) {
      644 +                        *ret_node = mnt_node;
      645 +                        if (fnode != NULL)
      646 +                                smb_node_release(fnode);
      647 +                } else {
      648 +                        *ret_node = fnode;
      649 +                }
 542  650  
 543  651                  if (dir_node)
 544  652                          *dir_node = dnode;
 545  653                  else
 546  654                          smb_node_release(dnode);
 547  655          }
 548  656  
 549  657          kmem_free(component, MAXNAMELEN);
 550  658          kmem_free(real_name, MAXNAMELEN);
 551  659          (void) pn_free(&pn);
 552  660          (void) pn_free(&rpn);
 553  661          (void) pn_free(&upn);
 554  662  
 555  663          return (err);
 556  664  }
 557  665  
 558  666  /*
 559  667   * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
 560  668   * and will be released within lookuppnvp().
 561  669   */
 562  670  static int
 563  671  smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags,
 564  672      vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred)
 565  673  {
 566  674          int err;
 567  675  
 568  676          *vp = NULL;
 569  677          VN_HOLD(dvp);
 570  678          if (rootvp != rootdir)
 571  679                  VN_HOLD(rootvp);
 572  680  
 573  681          err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred);
 574  682          if ((err == 0) && (attr != NULL))
 575  683                  (void) smb_vop_getattr(*vp, NULL, attr, 0, zone_kcred());
 576  684  
 577  685          return (err);
 578  686  }
 579  687  
 580  688  /*
 581  689   * CATIA Translation of a pathname component prior to passing it to lookuppnvp
 582  690   *
 583  691   * If the translated component name contains a '/' NULL is returned.
 584  692   * The caller should treat this as error EILSEQ. It is not valid to
 585  693   * have a directory name with a '/'.
 586  694   */
 587  695  static char *
 588  696  smb_pathname_catia_v5tov4(smb_request_t *sr, char *name,
 589  697      char *namebuf, int buflen)
 590  698  {
 591  699          char *namep;
 592  700  
 593  701          if (SMB_TREE_SUPPORTS_CATIA(sr)) {
 594  702                  namep = smb_vop_catia_v5tov4(name, namebuf, buflen);
 595  703                  if (strchr(namep, '/') != NULL)
 596  704                          return (NULL);
 597  705                  return (namep);
 598  706          }
 599  707  
 600  708          return (name);
 601  709  }
 602  710  
 603  711  /*
 604  712   * CATIA translation of a pathname component after returning from lookuppnvp
 605  713   */
 606  714  static char *
 607  715  smb_pathname_catia_v4tov5(smb_request_t *sr, char *name,
 608  716      char *namebuf, int buflen)
 609  717  {
 610  718          if (SMB_TREE_SUPPORTS_CATIA(sr)) {
 611  719                  smb_vop_catia_v4tov5(name, namebuf, buflen);
 612  720                  return (namebuf);
 613  721          }
 614  722  
 615  723          return (name);
 616  724  }
 617  725  
 618  726  /*
 619  727   * sr - needed to check for case sense
 620  728   * path - non mangled path needed to be looked up from the startvp
 621  729   * startvp - the vnode to start the lookup from
 622  730   * rootvp - the vnode of the root of the filesystem
 623  731   * returns the vnode found when starting at startvp and using the path
 624  732   *
 625  733   * Finds a vnode starting at startvp and parsing the non mangled path
 626  734   */
 627  735  
 628  736  vnode_t *
 629  737  smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp,
 630  738      vnode_t *rootvp)
 631  739  {
 632  740          pathname_t pn;
 633  741          vnode_t *vp = NULL;
 634  742          int lookup_flags = FOLLOW;
 635  743  
 636  744          if (SMB_TREE_IS_CASEINSENSITIVE(sr))
 637  745                  lookup_flags |= FIGNORECASE;
 638  746  
 639  747          (void) pn_alloc(&pn);
 640  748  
 641  749          if (pn_set(&pn, path) == 0) {
 642  750                  VN_HOLD(startvp);
 643  751                  if (rootvp != rootdir)
 644  752                          VN_HOLD(rootvp);
 645  753  
 646  754                  /* lookuppnvp should release the holds */
 647  755                  if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp,
 648  756                      rootvp, startvp, zone_kcred()) != 0) {
 649  757                          pn_free(&pn);
 650  758                          return (NULL);
 651  759                  }
 652  760          }
 653  761  
 654  762          pn_free(&pn);
 655  763          return (vp);
 656  764  }
 657  765  
 658  766  /*
 659  767   * smb_pathname_init
 660  768   * Parse path: pname\\fname:sname:stype
 661  769   *
 662  770   * Elements of the smb_pathname_t structure are allocated using request
 663  771   * specific storage and will be free'd when the sr is destroyed.
 664  772   *
 665  773   * Populate pn structure elements with the individual elements
 666  774   * of pn->pn_path. pn->pn_sname will contain the whole stream name
 667  775   * including the stream type and preceding colon: :sname:%DATA
 668  776   * pn_stype will point to the stream type within pn_sname.
 669  777   *
 670  778   * If the pname element is missing pn_pname will be set to NULL.
 671  779   * If any other element is missing the pointer in pn will be NULL.
 672  780   */
 673  781  void
 674  782  smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
 675  783  {
 676  784          char *pname, *fname, *sname;
 677  785          int len;
 678  786  
 679  787          bzero(pn, sizeof (smb_pathname_t));
 680  788          pn->pn_path = smb_pathname_strdup(sr, path);
 681  789  
 682  790          smb_pathname_preprocess(sr, pn);
 683  791  
 684  792          /* parse pn->pn_path into its constituent parts */
 685  793          pname = pn->pn_path;
 686  794          fname = strrchr(pn->pn_path, '\\');
 687  795  
 688  796          if (fname) {
 689  797                  if (fname == pname) {
 690  798                          pn->pn_pname = NULL;
 691  799                  } else {
 692  800                          *fname = '\0';
 693  801                          pn->pn_pname =
 694  802                              smb_pathname_strdup(sr, pname);
 695  803                          *fname = '\\';
 696  804                  }
 697  805                  ++fname;
 698  806          } else {
 699  807                  fname = pname;
 700  808                  pn->pn_pname = NULL;
 701  809          }
 702  810  
 703  811          if (fname[0] == '\0') {
 704  812                  pn->pn_fname = NULL;
 705  813                  return;
 706  814          }
 707  815  
 708  816          if (!smb_is_stream_name(fname)) {
 709  817                  pn->pn_fname = smb_pathname_strdup(sr, fname);
 710  818                  return;
 711  819          }
 712  820  
 713  821          /*
 714  822           * find sname and stype in fname.
 715  823           * sname can't be NULL smb_is_stream_name checks this
 716  824           */
 717  825          sname = strchr(fname, ':');
 718  826          if (sname == fname)
 719  827                  fname = NULL;
 720  828          else {
 721  829                  *sname = '\0';
 722  830                  pn->pn_fname =
 723  831                      smb_pathname_strdup(sr, fname);
 724  832                  *sname = ':';
 725  833          }
 726  834  
 727  835          pn->pn_sname = smb_pathname_strdup(sr, sname);
 728  836          pn->pn_stype = strchr(pn->pn_sname + 1, ':');
 729  837          if (pn->pn_stype) {
 730  838                  (void) smb_strupr(pn->pn_stype);
 731  839          } else {
 732  840                  len = strlen(pn->pn_sname);
 733  841                  pn->pn_sname = smb_pathname_strcat(sr, pn->pn_sname, ":$DATA");
 734  842                  pn->pn_stype = pn->pn_sname + len;
 735  843          }
 736  844          ++pn->pn_stype;
 737  845  }
 738  846  
 739  847  /*
 740  848   * smb_pathname_preprocess
 741  849   *
 742  850   * Perform common pre-processing of pn->pn_path:
 743  851   * - if the pn_path is blank, set it to '\\'
 744  852   * - perform unicode wildcard converstion.
 745  853   * - convert any '/' to '\\'
 746  854   * - eliminate duplicate slashes
 747  855   * - remove trailing slashes
 748  856   * - quota directory specific pre-processing
 749  857   */
 750  858  static void
 751  859  smb_pathname_preprocess(smb_request_t *sr, smb_pathname_t *pn)
 752  860  {
 753  861          char *p;
 754  862  
 755  863          /* treat empty path as "\\" */
 756  864          if (strlen(pn->pn_path) == 0) {
 757  865                  pn->pn_path = smb_pathname_strdup(sr, "\\");
 758  866                  return;
 759  867          }
 760  868  
 761  869          if (sr->session->dialect < NT_LM_0_12)
 762  870                  smb_convert_wildcards(pn->pn_path);
 763  871  
 764  872          /* treat '/' as '\\' */
 765  873          (void) strsubst(pn->pn_path, '/', '\\');
 766  874  
 767  875          (void) strcanon(pn->pn_path, "\\");
 768  876  
 769  877          /* remove trailing '\\' */
 770  878          p = pn->pn_path + strlen(pn->pn_path) - 1;
 771  879          if ((p != pn->pn_path) && (*p == '\\'))
 772  880                  *p = '\0';
 773  881  
 774  882          smb_pathname_preprocess_quota(sr, pn);
 775  883          smb_pathname_preprocess_adminshare(sr, pn);
 776  884  }
 777  885  
 778  886  /*
 779  887   * smb_pathname_preprocess_quota
 780  888   *
 781  889   * There is a special file required by windows so that the quota
 782  890   * tab will be displayed by windows clients. This is created in
 783  891   * a special directory, $EXTEND, at the root of the shared file
 784  892   * system. To hide this directory prepend a '.' (dot).
 785  893   */
 786  894  static void
 787  895  smb_pathname_preprocess_quota(smb_request_t *sr, smb_pathname_t *pn)
 788  896  {
 789  897          char *name = "$EXTEND";
 790  898          char *new_name = ".$EXTEND";
 791  899          char *p, *slash;
 792  900          int len;
 793  901  
 794  902          if (!smb_node_is_vfsroot(sr->tid_tree->t_snode))
 795  903                  return;
 796  904  
 797  905          p = pn->pn_path;
 798  906  
 799  907          /* ignore any initial "\\" */
 800  908          p += strspn(p, "\\");
 801  909          if (smb_strcasecmp(p, name, strlen(name)) != 0)
 802  910                  return;
 803  911  
 804  912          p += strlen(name);
 805  913          if ((*p != ':') && (*p != '\\') && (*p != '\0'))
 806  914                  return;
 807  915  
 808  916          slash = (pn->pn_path[0] == '\\') ? "\\" : "";
 809  917          len = strlen(pn->pn_path) + 2;
 810  918          pn->pn_path = smb_srm_alloc(sr, len);
 811  919          (void) snprintf(pn->pn_path, len, "%s%s%s", slash, new_name, p);
 812  920          (void) smb_strupr(pn->pn_path);
 813  921  }
 814  922  
 815  923  /*
 816  924   * smb_pathname_preprocess_adminshare
 817  925   *
 818  926   * Convert any path with share name "C$" or "c$" (Admin share) in to lower case.
 819  927   */
 820  928  static void
 821  929  smb_pathname_preprocess_adminshare(smb_request_t *sr, smb_pathname_t *pn)
 822  930  {
 823  931          if (strcasecmp(sr->tid_tree->t_sharename, "c$") == 0)
 824  932                  (void) smb_strlwr(pn->pn_path);
 825  933  }
 826  934  
 827  935  /*
 828  936   * smb_pathname_strdup
 829  937   *
 830  938   * Duplicate NULL terminated string s.
 831  939   *
 832  940   * The new string is allocated using request specific storage and will
 833  941   * be free'd when the sr is destroyed.
 834  942   */
 835  943  static char *
 836  944  smb_pathname_strdup(smb_request_t *sr, const char *s)
 837  945  {
 838  946          char *s2;
 839  947          size_t n;
 840  948  
 841  949          n = strlen(s) + 1;
 842  950          s2 = smb_srm_zalloc(sr, n);
 843  951          (void) strlcpy(s2, s, n);
 844  952          return (s2);
 845  953  }
 846  954  
 847  955  /*
 848  956   * smb_pathname_strcat
 849  957   *
 850  958   * Reallocate NULL terminated string s1 to accommodate
 851  959   * concatenating  NULL terminated string s2.
 852  960   * Append s2 and return resulting NULL terminated string.
 853  961   *
 854  962   * The string buffer is reallocated using request specific
 855  963   * storage and will be free'd when the sr is destroyed.
 856  964   */
 857  965  static char *
 858  966  smb_pathname_strcat(smb_request_t *sr, char *s1, const char *s2)
 859  967  {
 860  968          size_t n;
 861  969  
 862  970          n = strlen(s1) + strlen(s2) + 1;
 863  971          s1 = smb_srm_rezalloc(sr, s1, n);
 864  972          (void) strlcat(s1, s2, n);
 865  973          return (s1);
 866  974  }
 867  975  
 868  976  /*
 869  977   * smb_pathname_validate
 870  978   *
 871  979   * Perform basic validation of pn:
 872  980   * - If first component of pn->path is ".." -> PATH_SYNTAX_BAD
 873  981   * - If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID
 874  982   * - If fname is "." -> INVALID_OBJECT_NAME
 875  983   *
 876  984   * On unix .. at the root of a file system links to the root. Thus
 877  985   * an attempt to lookup "/../../.." will be the same as looking up "/"
 878  986   * CIFs clients expect the above to result in
 879  987   * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible
 880  988   * (and questionable if it's desirable) to deal with all cases
 881  989   * but paths beginning with \\.. are handled.
 882  990   *
 883  991   * Returns: B_TRUE if pn is valid,
 884  992   *          otherwise returns B_FALSE and sets error status in sr.
 885  993   *
 886  994   * XXX: Get rid of smbsr_error calls for SMB2
 887  995   */
 888  996  boolean_t
 889  997  smb_pathname_validate(smb_request_t *sr, smb_pathname_t *pn)
 890  998  {
 891  999          char *path = pn->pn_path;
 892 1000  
 893 1001          /* ignore any initial "\\" */
 894 1002          path += strspn(path, "\\");
 895 1003  
 896 1004          /* If first component of path is ".." -> PATH_SYNTAX_BAD */
 897 1005          if ((strcmp(path, "..") == 0) || (strncmp(path, "..\\", 3) == 0)) {
 898 1006                  smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
 899 1007                      ERRDOS, ERROR_BAD_PATHNAME);
  
    | 
      ↓ open down ↓ | 
    348 lines elided | 
    
      ↑ open up ↑ | 
  
 900 1008                  return (B_FALSE);
 901 1009          }
 902 1010  
 903 1011          /* If there are wildcards in pn->pn_pname -> OBJECT_NAME_INVALID */
 904 1012          if (pn->pn_pname && smb_contains_wildcards(pn->pn_pname)) {
 905 1013                  smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 906 1014                      ERRDOS, ERROR_INVALID_NAME);
 907 1015                  return (B_FALSE);
 908 1016          }
 909 1017  
 910      -        /* If fname is "." -> INVALID_OBJECT_NAME */
     1018 +        /* If fname is "." -> OBJECT_NAME_INVALID */
 911 1019          if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
 912 1020                  smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 913      -                    ERRDOS, ERROR_PATH_NOT_FOUND);
     1021 +                    ERRDOS, ERROR_INVALID_NAME);
 914 1022                  return (B_FALSE);
 915 1023          }
 916 1024  
 917 1025          return (B_TRUE);
 918 1026  }
 919 1027  
 920 1028  /*
 921 1029   * smb_validate_dirname
 922 1030   *
 923 1031   * smb_pathname_validate() should have already been performed on pn.
 924 1032   *
 925 1033   * Very basic directory name validation:  checks for colons in a path.
 926 1034   * Need to skip the drive prefix since it contains a colon.
 927 1035   *
 928 1036   * Returns: B_TRUE if the name is valid,
 929 1037   *          otherwise returns B_FALSE and sets error status in sr.
 930 1038   */
 931 1039  boolean_t
 932 1040  smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
 933 1041  {
 934 1042          char *name;
 935 1043          char *path = pn->pn_path;
 936 1044  
 937 1045          if ((name = path) != 0) {
 938 1046                  name += strspn(name, "\\");
 939 1047  
 940 1048                  if (strchr(name, ':') != 0) {
 941 1049                          smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
 942 1050                              ERRDOS, ERROR_INVALID_NAME);
 943 1051                          return (B_FALSE);
 944 1052                  }
 945 1053          }
 946 1054  
 947 1055          return (B_TRUE);
 948 1056  }
 949 1057  
 950 1058  /*
 951 1059   * smb_validate_object_name
 952 1060   *
 953 1061   * smb_pathname_validate() should have already been pertformed on pn.
 954 1062   *
 955 1063   * Very basic file name validation.
 956 1064   * For filenames, we check for names of the form "AAAn:". Names that
 957 1065   * contain three characters, a single digit and a colon (:) are reserved
 958 1066   * as DOS device names, i.e. "COM1:".
 959 1067   * Stream name validation is handed off to smb_validate_stream_name
 960 1068   *
 961 1069   * Returns: B_TRUE if pn->pn_fname is valid,
 962 1070   *          otherwise returns B_FALSE and sets error status in sr.
 963 1071   */
 964 1072  boolean_t
 965 1073  smb_validate_object_name(smb_request_t *sr, smb_pathname_t *pn)
 966 1074  {
 967 1075          if (pn->pn_fname &&
 968 1076              strlen(pn->pn_fname) == 5 &&
 969 1077              smb_isdigit(pn->pn_fname[3]) &&
 970 1078              pn->pn_fname[4] == ':') {
 971 1079                  smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 972 1080                      ERRDOS, ERROR_INVALID_NAME);
 973 1081                  return (B_FALSE);
 974 1082          }
 975 1083  
 976 1084          if (pn->pn_sname)
  
    | 
      ↓ open down ↓ | 
    53 lines elided | 
    
      ↑ open up ↑ | 
  
 977 1085                  return (smb_validate_stream_name(sr, pn));
 978 1086  
 979 1087          return (B_TRUE);
 980 1088  }
 981 1089  
 982 1090  /*
 983 1091   * smb_stream_parse_name
 984 1092   *
 985 1093   * smb_stream_parse_name should only be called for a path that
 986 1094   * contains a valid named stream.  Path validation should have
 987      - * been performed before this function is called.
     1095 + * been performed before this function is called, typically by
     1096 + * calling smb_is_stream_name() just before this.
 988 1097   *
 989 1098   * Find the last component of path and split it into filename
 990 1099   * and stream name.
 991 1100   *
 992 1101   * On return the named stream type will be present.  The stream
 993 1102   * type defaults to ":$DATA", if it has not been defined
 994      - * For exmaple, 'stream' contains :<sname>:$DATA
     1103 + * For example, 'stream' contains :<sname>:$DATA
     1104 + *
     1105 + * Output args: filename, stream both MAXNAMELEN
 995 1106   */
 996 1107  void
 997 1108  smb_stream_parse_name(char *path, char *filename, char *stream)
 998 1109  {
 999 1110          char *fname, *sname, *stype;
     1111 +        size_t flen, slen;
1000 1112  
1001 1113          ASSERT(path);
1002 1114          ASSERT(filename);
1003 1115          ASSERT(stream);
1004 1116  
1005 1117          fname = strrchr(path, '\\');
1006 1118          fname = (fname == NULL) ? path : fname + 1;
1007      -        (void) strlcpy(filename, fname, MAXNAMELEN);
     1119 +        sname = strchr(fname, ':');
     1120 +        /* Caller makes sure there is a ':' in path. */
     1121 +        VERIFY(sname != NULL);
     1122 +        /* LINTED: possible ptrdiff_t overflow */
     1123 +        flen = sname - fname;
     1124 +        slen = strlen(sname);
1008 1125  
1009      -        sname = strchr(filename, ':');
1010      -        (void) strlcpy(stream, sname, MAXNAMELEN);
1011      -        *sname = '\0';
     1126 +        if (flen > (MAXNAMELEN-1))
     1127 +                flen = (MAXNAMELEN-1);
     1128 +        (void) strncpy(filename, fname, flen);
     1129 +        filename[flen] = '\0';
1012 1130  
     1131 +        if (slen > (MAXNAMELEN-1))
     1132 +                slen = (MAXNAMELEN-1);
     1133 +        (void) strncpy(stream, sname, slen);
     1134 +        stream[slen] = '\0';
     1135 +
     1136 +        /* Add a "stream type" if there isn't one. */
1013 1137          stype = strchr(stream + 1, ':');
1014 1138          if (stype == NULL)
1015 1139                  (void) strlcat(stream, ":$DATA", MAXNAMELEN);
1016 1140          else
1017 1141                  (void) smb_strupr(stype);
1018 1142  }
1019 1143  
1020 1144  /*
1021 1145   * smb_is_stream_name
1022 1146   *
1023 1147   * Determines if 'path' specifies a named stream.
1024 1148   *
1025 1149   * path is a NULL terminated string which could be a stream path.
1026 1150   * [pathname/]fname[:stream_name[:stream_type]]
1027 1151   *
1028 1152   * - If there is no colon in the path or it's the last char
1029 1153   *   then it's not a stream name
1030 1154   *
1031 1155   * - '::' is a non-stream and is commonly used by Windows to designate
1032 1156   *   the unamed stream in the form "::$DATA"
1033 1157   */
1034 1158  boolean_t
1035 1159  smb_is_stream_name(char *path)
1036 1160  {
1037 1161          char *colonp;
1038 1162  
1039 1163          if (path == NULL)
1040 1164                  return (B_FALSE);
1041 1165  
1042 1166          colonp = strchr(path, ':');
  
    | 
      ↓ open down ↓ | 
    20 lines elided | 
    
      ↑ open up ↑ | 
  
1043 1167          if ((colonp == NULL) || (*(colonp+1) == '\0'))
1044 1168                  return (B_FALSE);
1045 1169  
1046 1170          if (strstr(path, "::"))
1047 1171                  return (B_FALSE);
1048 1172  
1049 1173          return (B_TRUE);
1050 1174  }
1051 1175  
1052 1176  /*
     1177 + * Is this stream node a "restricted" type?
     1178 + */
     1179 +boolean_t
     1180 +smb_strname_restricted(char *strname)
     1181 +{
     1182 +        char *stype;
     1183 +
     1184 +        stype = strrchr(strname, ':');
     1185 +        if (stype == NULL)
     1186 +                return (B_FALSE);
     1187 +
     1188 +        /*
     1189 +         * Only ":$CA" is restricted (for now).
     1190 +         */
     1191 +        if (strcmp(stype, ":$CA") == 0)
     1192 +                return (B_TRUE);
     1193 +
     1194 +        return (B_FALSE);
     1195 +}
     1196 +
     1197 +/*
1053 1198   * smb_validate_stream_name
1054 1199   *
1055 1200   * B_FALSE will be returned, and the error status ser in the sr, if:
1056 1201   * - the path is not a stream name
1057 1202   * - a path is specified but the fname is ommitted.
1058 1203   * - the stream_type is specified but not valid.
1059 1204   *
1060 1205   * Note: the stream type is case-insensitive.
1061 1206   */
1062 1207  boolean_t
1063 1208  smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
1064 1209  {
1065 1210          static char *strmtype[] = {
     1211 +                "$CA",
1066 1212                  "$DATA",
1067 1213                  "$INDEX_ALLOCATION"
1068 1214          };
1069 1215          int i;
1070 1216  
1071 1217          ASSERT(pn);
1072 1218          ASSERT(pn->pn_sname);
1073 1219  
1074 1220          if ((!(pn->pn_sname)) ||
1075 1221              ((pn->pn_pname) && !(pn->pn_fname))) {
1076 1222                  smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1077 1223                      ERRDOS, ERROR_INVALID_NAME);
1078 1224                  return (B_FALSE);
1079 1225          }
1080 1226  
1081 1227  
1082 1228          if (pn->pn_stype != NULL) {
1083 1229                  for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
1084 1230                          if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
1085 1231                                  return (B_TRUE);
1086 1232                  }
1087 1233  
1088 1234                  smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
1089 1235                      ERRDOS, ERROR_INVALID_NAME);
1090 1236                  return (B_FALSE);
1091 1237          }
1092 1238  
1093 1239          return (B_TRUE);
1094 1240  }
1095 1241  
1096 1242  /*
1097 1243   * valid DFS I/O path:
1098 1244   *
1099 1245   * \server-or-domain\share
1100 1246   * \server-or-domain\share\path
1101 1247   *
1102 1248   * All the returned errors by this function needs to be
1103 1249   * checked against Windows.
1104 1250   */
1105 1251  static int
1106 1252  smb_pathname_dfs_preprocess(smb_request_t *sr, char *path, size_t pathsz)
1107 1253  {
1108 1254          smb_unc_t unc;
1109 1255          char *linkpath;
1110 1256          int rc;
1111 1257  
1112 1258          if (sr->tid_tree == NULL)
1113 1259                  return (0);
1114 1260  
1115 1261          if ((rc = smb_unc_init(path, &unc)) != 0)
1116 1262                  return (rc);
1117 1263  
1118 1264          if (smb_strcasecmp(unc.unc_share, sr->tid_tree->t_sharename, 0)) {
1119 1265                  smb_unc_free(&unc);
1120 1266                  return (EINVAL);
1121 1267          }
1122 1268  
1123 1269          linkpath = unc.unc_path;
1124 1270          (void) snprintf(path, pathsz, "/%s", (linkpath) ? linkpath : "");
1125 1271  
1126 1272          smb_unc_free(&unc);
1127 1273          return (0);
1128 1274  }
  
    | 
      ↓ open down ↓ | 
    53 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX