Print this page
    
NEX-6096 Enable compile warnings re. parentheses in smbsrv
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
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>
SMB-50 User-mode SMB server (fix vnodetopath)
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/smbsrv/libfksmbsrv/common/fake_lookup.c
          +++ new/usr/src/lib/smbsrv/libfksmbsrv/common/fake_lookup.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) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  25   25   */
  26   26  
  27   27  /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
  28   28  /*        All Rights Reserved   */
  29   29  
  30   30  /*
  31   31   * University Copyright- Copyright (c) 1982, 1986, 1988
  32   32   * The Regents of the University of California
  33   33   * All Rights Reserved
  34   34   *
  35   35   * University Acknowledgment- Portions of this document are derived from
  36   36   * software developed by the University of California, Berkeley, and its
  37   37   * contributors.
  38   38   */
  39   39  
  40   40  #include <sys/types.h>
  41   41  #include <sys/param.h>
  42   42  #include <sys/systm.h>
  43   43  #include <sys/file.h>
  44   44  #include <sys/errno.h>
  45   45  #include <sys/cred.h>
  46   46  #include <sys/user.h>
  47   47  #include <sys/uio.h>
  48   48  #include <sys/vfs.h>
  49   49  #include <sys/vnode.h>
  50   50  #include <sys/pathname.h>
  51   51  #include <sys/proc.h>
  52   52  #include <sys/vtrace.h>
  53   53  #include <sys/sysmacros.h>
  54   54  #include <sys/debug.h>
  55   55  #include <sys/dirent.h>
  56   56  #include <sys/zone.h>
  57   57  #include <sys/dnlc.h>
  58   58  #include <sys/fs/snode.h>
  59   59  
  60   60  /*
  61   61   * Starting at current directory, translate pathname pnp to end.
  62   62   * Leave pathname of final component in pnp, return the vnode
  63   63   * for the final component in *compvpp, and return the vnode
  64   64   * for the parent of the final component in dirvpp.
  65   65   *
  66   66   * This is the central routine in pathname translation and handles
  67   67   * multiple components in pathnames, separating them at /'s.  It also
  68   68   * implements mounted file systems and processes symbolic links.
  69   69   *
  70   70   * vp is the vnode where the directory search should start.
  71   71   *
  72   72   * Reference counts: vp must be held prior to calling this function.  rootvp
  73   73   * should only be held if rootvp != rootdir.
  74   74   */
  75   75  int
  76   76  lookuppnvp(
  77   77          struct pathname *pnp,           /* pathname to lookup */
  78   78          struct pathname *rpnp,          /* if non-NULL, return resolved path */
  79   79          int flags,                      /* follow symlinks */
  80   80          vnode_t **dirvpp,               /* ptr for parent vnode */
  81   81          vnode_t **compvpp,              /* ptr for entry vnode */
  82   82          vnode_t *rootvp,                /* rootvp */
  83   83          vnode_t *vp,                    /* directory to start search at */
  84   84          cred_t *cr)                     /* user's credential */
  85   85  {
  86   86          vnode_t *cvp;   /* current component vp */
  87   87          vnode_t *tvp;   /* addressable temp ptr */
  88   88          char component[MAXNAMELEN];     /* buffer for component (incl null) */
  89   89          int error;
  90   90          int nlink;
  91   91          int lookup_flags;
  92   92          struct pathname presrvd; /* case preserved name */
  93   93          struct pathname *pp = NULL;
  94   94          vnode_t *startvp;
  95   95          int must_be_directory = 0;
  96   96          boolean_t retry_with_kcred;
  97   97  
  98   98          nlink = 0;
  99   99          cvp = NULL;
 100  100          if (rpnp)
 101  101                  rpnp->pn_pathlen = 0;
 102  102  
 103  103          lookup_flags = dirvpp ? LOOKUP_DIR : 0;
 104  104          if (flags & FIGNORECASE) {
 105  105                  lookup_flags |= FIGNORECASE;
 106  106                  pn_alloc(&presrvd);
 107  107                  pp = &presrvd;
 108  108          }
 109  109  
 110  110          /*
 111  111           * Eliminate any trailing slashes in the pathname.
 112  112           * If there are any, we must follow all symlinks.
 113  113           * Also, we must guarantee that the last component is a directory.
 114  114           */
 115  115          if (pn_fixslash(pnp)) {
 116  116                  flags |= FOLLOW;
 117  117                  must_be_directory = 1;
 118  118          }
 119  119  
 120  120          startvp = vp;
 121  121  next:
 122  122          retry_with_kcred = B_FALSE;
 123  123  
 124  124          /*
 125  125           * Make sure we have a directory.
 126  126           */
 127  127          if (vp->v_type != VDIR) {
  
    | 
      ↓ open down ↓ | 
    127 lines elided | 
    
      ↑ open up ↑ | 
  
 128  128                  error = ENOTDIR;
 129  129                  goto bad;
 130  130          }
 131  131  
 132  132          if (rpnp && VN_CMP(vp, rootvp))
 133  133                  (void) pn_set(rpnp, "/");
 134  134  
 135  135          /*
 136  136           * Process the next component of the pathname.
 137  137           */
 138      -        if (error = pn_getcomponent(pnp, component)) {
      138 +        if ((error = pn_getcomponent(pnp, component)) != 0) {
 139  139                  goto bad;
 140  140          }
 141  141  
 142  142          /*
 143  143           * Handle "..": two special cases.
 144  144           * 1. If we're at the root directory (e.g. after chroot or
 145  145           *    zone_enter) then change ".." to "." so we can't get
 146  146           *    out of this subtree.
 147  147           * 2. If this vnode is the root of a mounted file system,
 148  148           *    then replace it with the vnode that was mounted on
 149  149           *    so that we take the ".." in the other file system.
 150  150           */
 151  151          if (component[0] == '.' && component[1] == '.' && component[2] == 0) {
 152  152  checkforroot:
 153  153                  if (VN_CMP(vp, rootvp)) {
 154  154                          component[1] = '\0';
 155  155                  } else if (vp->v_flag & VROOT) {
 156  156                          vfs_t *vfsp;
 157  157                          cvp = vp;
 158  158  
 159  159                          /*
 160  160                           * While we deal with the vfs pointer from the vnode
 161  161                           * the filesystem could have been forcefully unmounted
 162  162                           * and the vnode's v_vfsp could have been invalidated
 163  163                           * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it
 164  164                           * with vfs_rlock_wait/vfs_unlock.
 165  165                           * It is safe to use the v_vfsp even it is freed by
 166  166                           * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock
 167  167                           * do not dereference v_vfsp. It is just used as a
 168  168                           * magic cookie.
 169  169                           * One more corner case here is the memory getting
 170  170                           * reused for another vfs structure. In this case
 171  171                           * lookuppnvp's vfs_rlock_wait will succeed, domount's
 172  172                           * vfs_lock will fail and domount will bail out with an
 173  173                           * error (EBUSY).
 174  174                           */
 175  175                          vfsp = cvp->v_vfsp;
 176  176  
 177  177                          /*
 178  178                           * This lock is used to synchronize
 179  179                           * mounts/unmounts and lookups.
 180  180                           * Threads doing mounts/unmounts hold the
 181  181                           * writers version vfs_lock_wait().
 182  182                           */
 183  183  
 184  184                          vfs_rlock_wait(vfsp);
 185  185  
 186  186                          /*
 187  187                           * If this vnode is on a file system that
 188  188                           * has been forcibly unmounted,
 189  189                           * we can't proceed. Cancel this operation
 190  190                           * and return EIO.
 191  191                           *
 192  192                           * vfs_vnodecovered is NULL if unmounted.
 193  193                           * Currently, nfs uses VFS_UNMOUNTED to
 194  194                           * check if it's a forced-umount. Keep the
 195  195                           * same checking here as well even though it
 196  196                           * may not be needed.
 197  197                           */
 198  198                          if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
 199  199                              (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
 200  200                                  vfs_unlock(vfsp);
 201  201                                  VN_RELE(cvp);
 202  202                                  if (pp)
 203  203                                          pn_free(pp);
 204  204                                  return (EIO);
 205  205                          }
 206  206                          VN_HOLD(vp);
 207  207                          vfs_unlock(vfsp);
 208  208                          VN_RELE(cvp);
 209  209                          cvp = NULL;
 210  210                          /*
 211  211                           * Crossing mount points. For eg: We are doing
 212  212                           * a lookup of ".." for file systems root vnode
 213  213                           * mounted here, and VOP_LOOKUP() (with covered vnode)
 214  214                           * will be on underlying file systems mount point
 215  215                           * vnode. Set retry_with_kcred flag as we might end
 216  216                           * up doing VOP_LOOKUP() with kcred if required.
 217  217                           */
 218  218                          retry_with_kcred = B_TRUE;
 219  219                          goto checkforroot;
 220  220                  }
 221  221          }
 222  222  
 223  223          /*
 224  224           * Perform a lookup in the current directory.
 225  225           */
 226  226          error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
 227  227              rootvp, cr, NULL, NULL, pp);
 228  228  
 229  229          /*
 230  230           * Retry with kcred - If crossing mount points & error is EACCES.
 231  231           *
 232  232           * If we are crossing mount points here and doing ".." lookup,
 233  233           * VOP_LOOKUP() might fail if the underlying file systems
 234  234           * mount point has no execute permission. In cases like these,
 235  235           * we retry VOP_LOOKUP() by giving as much privilage as possible
 236  236           * by passing kcred credentials.
 237  237           *
 238  238           * In case of hierarchical file systems, passing kcred still may
 239  239           * or may not work.
 240  240           * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some
 241  241           *                      directory inside NFS FS.
 242  242           */
 243  243          if ((error == EACCES) && retry_with_kcred)
 244  244                  error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
 245  245                      rootvp, zone_kcred(), NULL, NULL, pp);
 246  246  
 247  247          cvp = tvp;
 248  248          if (error) {
 249  249                  cvp = NULL;
 250  250                  /*
 251  251                   * On error, return hard error if
 252  252                   * (a) we're not at the end of the pathname yet, or
 253  253                   * (b) the caller didn't want the parent directory, or
 254  254                   * (c) we failed for some reason other than a missing entry.
 255  255                   */
 256  256                  if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT)
 257  257                          goto bad;
 258  258  
 259  259                  pn_setlast(pnp);
 260  260                  /*
 261  261                   * We inform the caller that the desired entry must be
 262  262                   * a directory by adding a '/' to the component name.
 263  263                   */
 264  264                  if (must_be_directory && (error = pn_addslash(pnp)) != 0)
 265  265                          goto bad;
 266  266                  *dirvpp = vp;
 267  267                  if (compvpp != NULL)
 268  268                          *compvpp = NULL;
 269  269                  if (rootvp != rootdir)
 270  270                          VN_RELE(rootvp);
 271  271                  if (pp)
 272  272                          pn_free(pp);
 273  273                  return (0);
 274  274          }
 275  275  
 276  276          /*
 277  277           * Traverse mount points.
 278  278           */
 279  279          if (vn_mountedvfs(cvp) != NULL) {
 280  280                  tvp = cvp;
 281  281                  if ((error = traverse(&tvp)) != 0) {
 282  282                          /*
 283  283                           * It is required to assign cvp here, because
 284  284                           * traverse() will return a held vnode which
 285  285                           * may different than the vnode that was passed
 286  286                           * in (even in the error case).  If traverse()
 287  287                           * changes the vnode it releases the original,
 288  288                           * and holds the new one.
 289  289                           */
 290  290                          cvp = tvp;
 291  291                          goto bad;
 292  292                  }
 293  293                  cvp = tvp;
 294  294          }
 295  295  
 296  296          /*
 297  297           * If we hit a symbolic link and there is more path to be
 298  298           * translated or this operation does not wish to apply
 299  299           * to a link, then place the contents of the link at the
  
    | 
      ↓ open down ↓ | 
    151 lines elided | 
    
      ↑ open up ↑ | 
  
 300  300           * front of the remaining pathname.
 301  301           */
 302  302          if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) {
 303  303                  struct pathname linkpath;
 304  304  
 305  305                  if (++nlink > MAXSYMLINKS) {
 306  306                          error = ELOOP;
 307  307                          goto bad;
 308  308                  }
 309  309                  pn_alloc(&linkpath);
 310      -                if (error = pn_getsymlink(cvp, &linkpath, cr)) {
      310 +                if ((error = pn_getsymlink(cvp, &linkpath, cr)) != 0) {
 311  311                          pn_free(&linkpath);
 312  312                          goto bad;
 313  313                  }
 314  314  
 315  315                  if (pn_pathleft(&linkpath) == 0)
 316  316                          (void) pn_set(&linkpath, ".");
 317  317                  error = pn_insert(pnp, &linkpath, strlen(component));
 318  318                  pn_free(&linkpath);
 319  319                  if (error)
 320  320                          goto bad;
 321  321                  VN_RELE(cvp);
 322  322                  cvp = NULL;
 323  323                  if (pnp->pn_pathlen == 0) {
 324  324                          error = ENOENT;
 325  325                          goto bad;
 326  326                  }
 327  327                  if (pnp->pn_path[0] == '/') {
 328  328                          do {
 329  329                                  pnp->pn_path++;
 330  330                                  pnp->pn_pathlen--;
 331  331                          } while (pnp->pn_path[0] == '/');
 332  332                          VN_RELE(vp);
 333  333                          vp = rootvp;
 334  334                          VN_HOLD(vp);
 335  335                  }
 336  336                  if (pn_fixslash(pnp)) {
 337  337                          flags |= FOLLOW;
 338  338                          must_be_directory = 1;
 339  339                  }
 340  340                  goto next;
 341  341          }
 342  342  
 343  343          /*
 344  344           * If rpnp is non-NULL, remember the resolved path name therein.
 345  345           * Do not include "." components.  Collapse occurrences of
 346  346           * "previous/..", so long as "previous" is not itself "..".
 347  347           * Exhausting rpnp results in error ENAMETOOLONG.
 348  348           */
 349  349          if (rpnp && strcmp(component, ".") != 0) {
 350  350                  size_t len;
 351  351  
 352  352                  if (strcmp(component, "..") == 0 &&
 353  353                      rpnp->pn_pathlen != 0 &&
 354  354                      !((rpnp->pn_pathlen > 2 &&
 355  355                      strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) ||
 356  356                      (rpnp->pn_pathlen == 2 &&
 357  357                      strncmp(rpnp->pn_path, "..", 2) == 0))) {
 358  358                          while (rpnp->pn_pathlen &&
 359  359                              rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
 360  360                                  rpnp->pn_pathlen--;
 361  361                          if (rpnp->pn_pathlen > 1)
 362  362                                  rpnp->pn_pathlen--;
 363  363                          rpnp->pn_path[rpnp->pn_pathlen] = '\0';
 364  364                  } else {
 365  365                          if (rpnp->pn_pathlen != 0 &&
 366  366                              rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
 367  367                                  rpnp->pn_path[rpnp->pn_pathlen++] = '/';
 368  368                          if (flags & FIGNORECASE) {
 369  369                                  /*
 370  370                                   * Return the case-preserved name
 371  371                                   * within the resolved path.
 372  372                                   */
 373  373                                  error = copystr(pp->pn_buf,
 374  374                                      rpnp->pn_path + rpnp->pn_pathlen,
 375  375                                      rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
 376  376                          } else {
 377  377                                  error = copystr(component,
 378  378                                      rpnp->pn_path + rpnp->pn_pathlen,
 379  379                                      rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
 380  380                          }
 381  381                          if (error)      /* copystr() returns ENAMETOOLONG */
 382  382                                  goto bad;
 383  383                          rpnp->pn_pathlen += (len - 1);
 384  384                          ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen);
 385  385                  }
 386  386          }
 387  387  
 388  388          /*
 389  389           * If no more components, return last directory (if wanted) and
 390  390           * last component (if wanted).
 391  391           */
 392  392          if (pn_pathleft(pnp) == 0) {
 393  393                  /*
 394  394                   * If there was a trailing slash in the pathname,
 395  395                   * make sure the last component is a directory.
 396  396                   */
 397  397                  if (must_be_directory && cvp->v_type != VDIR) {
 398  398                          error = ENOTDIR;
 399  399                          goto bad;
 400  400                  }
 401  401                  if (dirvpp != NULL) {
 402  402                          /*
 403  403                           * Check that we have the real parent and not
 404  404                           * an alias of the last component.
 405  405                           */
 406  406                          if (vn_compare(vp, cvp)) {
 407  407                                  pn_setlast(pnp);
 408  408                                  VN_RELE(vp);
 409  409                                  VN_RELE(cvp);
 410  410                                  if (rootvp != rootdir)
 411  411                                          VN_RELE(rootvp);
 412  412                                  if (pp)
 413  413                                          pn_free(pp);
 414  414                                  return (EINVAL);
 415  415                          }
 416  416                          *dirvpp = vp;
 417  417                  } else
 418  418                          VN_RELE(vp);
 419  419                  if (pnp->pn_path == pnp->pn_buf)
 420  420                          (void) pn_set(pnp, ".");
 421  421                  else
 422  422                          pn_setlast(pnp);
 423  423                  if (rpnp) {
 424  424                          if (VN_CMP(cvp, rootvp))
 425  425                                  (void) pn_set(rpnp, "/");
 426  426                          else if (rpnp->pn_pathlen == 0)
 427  427                                  (void) pn_set(rpnp, ".");
 428  428                  }
 429  429  
 430  430                  if (compvpp != NULL)
 431  431                          *compvpp = cvp;
 432  432                  else
 433  433                          VN_RELE(cvp);
 434  434                  if (rootvp != rootdir)
 435  435                          VN_RELE(rootvp);
 436  436                  if (pp)
 437  437                          pn_free(pp);
 438  438                  return (0);
 439  439          }
 440  440  
 441  441          /*
 442  442           * Skip over slashes from end of last component.
 443  443           */
 444  444          while (pnp->pn_path[0] == '/') {
 445  445                  pnp->pn_path++;
 446  446                  pnp->pn_pathlen--;
 447  447          }
 448  448  
 449  449          /*
 450  450           * Searched through another level of directory:
 451  451           * release previous directory handle and save new (result
 452  452           * of lookup) as current directory.
 453  453           */
 454  454          VN_RELE(vp);
 455  455          vp = cvp;
 456  456          cvp = NULL;
 457  457          goto next;
 458  458  
 459  459  bad:
 460  460          /*
 461  461           * Error.  Release vnodes and return.
 462  462           */
 463  463          if (cvp)
 464  464                  VN_RELE(cvp);
 465  465          /*
 466  466           * If the error was ESTALE and the current directory to look in
 467  467           * was the root for this lookup, the root for a mounted file
 468  468           * system, or the starting directory for lookups, then
 469  469           * return ENOENT instead of ESTALE.  In this case, no recovery
 470  470           * is possible by the higher level.  If ESTALE was returned for
 471  471           * some intermediate directory along the path, then recovery
 472  472           * is potentially possible and retrying from the higher level
 473  473           * will either correct the situation by purging stale cache
 474  474           * entries or eventually get back to the point where no recovery
 475  475           * is possible.
 476  476           */
 477  477          if (error == ESTALE &&
 478  478              (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp))
 479  479                  error = ENOENT;
 480  480          VN_RELE(vp);
 481  481          if (rootvp != rootdir)
 482  482                  VN_RELE(rootvp);
 483  483          if (pp)
 484  484                  pn_free(pp);
 485  485          return (error);
 486  486  }
 487  487  
 488  488  /*
 489  489   * Traverse a mount point.  Routine accepts a vnode pointer as a reference
 490  490   * parameter and performs the indirection, releasing the original vnode.
 491  491   */
 492  492  int
 493  493  traverse(vnode_t **cvpp)
 494  494  {
 495  495          int error = 0;
 496  496          vnode_t *cvp;
 497  497          vnode_t *tvp;
 498  498          vfs_t *vfsp;
 499  499  
 500  500          cvp = *cvpp;
 501  501  
 502  502          /*
 503  503           * If this vnode is mounted on, then we transparently indirect
 504  504           * to the vnode which is the root of the mounted file system.
 505  505           * Before we do this we must check that an unmount is not in
 506  506           * progress on this vnode.
 507  507           */
 508  508  
 509  509          for (;;) {
 510  510                  /*
 511  511                   * Used to try to read lock the vnode here.
 512  512                   */
 513  513  
 514  514                  /*
 515  515                   * Reached the end of the mount chain?
 516  516                   */
 517  517                  vfsp = vn_mountedvfs(cvp);
 518  518                  if (vfsp == NULL) {
 519  519                          break;
 520  520                  }
 521  521  
 522  522                  /*
 523  523                   * The read lock must be held across the call to VFS_ROOT() to
 524  524                   * prevent a concurrent unmount from destroying the vfs.
 525  525                   */
 526  526                  error = VFS_ROOT(vfsp, &tvp);
 527  527                  if (error)
 528  528                          break;
 529  529  
 530  530                  VN_RELE(cvp);
 531  531  
 532  532                  cvp = tvp;
 533  533          }
 534  534  
 535  535          *cvpp = cvp;
 536  536          return (error);
 537  537  }
 538  538  
 539  539  /*
 540  540   * Get the vnode path, relative to the passed rootvp.
 541  541   * Our vncache always fills in v_path, so this is easy.
 542  542   */
 543  543  /* ARGSUSED */
 544  544  int
 545  545  vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr)
 546  546  {
 547  547          int len, rvp_len = 0;
 548  548          const char *p = vp->v_path;
 549  549  
 550  550          if (vrootp)
 551  551                  rvp_len = strlen(vrootp->v_path);
 552  552          len = strlen(p);
 553  553          if (rvp_len < len)
 554  554                  p += rvp_len;
 555  555          else
 556  556                  p = "/";
 557  557  
 558  558          (void) strlcpy(buf, p, buflen);
 559  559          return (0);
 560  560  }
  
    | 
      ↓ open down ↓ | 
    240 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX