1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28*7c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 29*7c478bd9Sstevel@tonic-gate 30*7c478bd9Sstevel@tonic-gate /* 31*7c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 32*7c478bd9Sstevel@tonic-gate * The Regents of the University of California 33*7c478bd9Sstevel@tonic-gate * All Rights Reserved 34*7c478bd9Sstevel@tonic-gate * 35*7c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 36*7c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 37*7c478bd9Sstevel@tonic-gate * contributors. 38*7c478bd9Sstevel@tonic-gate */ 39*7c478bd9Sstevel@tonic-gate 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 42*7c478bd9Sstevel@tonic-gate 43*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 44*7c478bd9Sstevel@tonic-gate #include <sys/param.h> 45*7c478bd9Sstevel@tonic-gate #include <sys/systm.h> 46*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 47*7c478bd9Sstevel@tonic-gate #include <sys/errno.h> 48*7c478bd9Sstevel@tonic-gate #include <sys/cred.h> 49*7c478bd9Sstevel@tonic-gate #include <sys/user.h> 50*7c478bd9Sstevel@tonic-gate #include <sys/uio.h> 51*7c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 52*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 53*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 54*7c478bd9Sstevel@tonic-gate #include <sys/proc.h> 55*7c478bd9Sstevel@tonic-gate #include <sys/vtrace.h> 56*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 57*7c478bd9Sstevel@tonic-gate #include <sys/debug.h> 58*7c478bd9Sstevel@tonic-gate #include <sys/dirent.h> 59*7c478bd9Sstevel@tonic-gate #include <c2/audit.h> 60*7c478bd9Sstevel@tonic-gate #include <sys/zone.h> 61*7c478bd9Sstevel@tonic-gate #include <sys/dnlc.h> 62*7c478bd9Sstevel@tonic-gate #include <sys/fs/snode.h> 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate /* Controls whether paths are stored with vnodes. */ 65*7c478bd9Sstevel@tonic-gate int vfs_vnode_path = 1; 66*7c478bd9Sstevel@tonic-gate 67*7c478bd9Sstevel@tonic-gate int 68*7c478bd9Sstevel@tonic-gate lookupname( 69*7c478bd9Sstevel@tonic-gate char *fnamep, 70*7c478bd9Sstevel@tonic-gate enum uio_seg seg, 71*7c478bd9Sstevel@tonic-gate enum symfollow followlink, 72*7c478bd9Sstevel@tonic-gate vnode_t **dirvpp, 73*7c478bd9Sstevel@tonic-gate vnode_t **compvpp) 74*7c478bd9Sstevel@tonic-gate { 75*7c478bd9Sstevel@tonic-gate return (lookupnameat(fnamep, seg, followlink, dirvpp, compvpp, NULL)); 76*7c478bd9Sstevel@tonic-gate } 77*7c478bd9Sstevel@tonic-gate 78*7c478bd9Sstevel@tonic-gate 79*7c478bd9Sstevel@tonic-gate /* 80*7c478bd9Sstevel@tonic-gate * Lookup the user file name, 81*7c478bd9Sstevel@tonic-gate * Handle allocation and freeing of pathname buffer, return error. 82*7c478bd9Sstevel@tonic-gate */ 83*7c478bd9Sstevel@tonic-gate int 84*7c478bd9Sstevel@tonic-gate lookupnameat( 85*7c478bd9Sstevel@tonic-gate char *fnamep, /* user pathname */ 86*7c478bd9Sstevel@tonic-gate enum uio_seg seg, /* addr space that name is in */ 87*7c478bd9Sstevel@tonic-gate enum symfollow followlink, /* follow sym links */ 88*7c478bd9Sstevel@tonic-gate vnode_t **dirvpp, /* ret for ptr to parent dir vnode */ 89*7c478bd9Sstevel@tonic-gate vnode_t **compvpp, /* ret for ptr to component vnode */ 90*7c478bd9Sstevel@tonic-gate vnode_t *startvp) /* start path search from vp */ 91*7c478bd9Sstevel@tonic-gate { 92*7c478bd9Sstevel@tonic-gate char namebuf[TYPICALMAXPATHLEN]; 93*7c478bd9Sstevel@tonic-gate struct pathname lookpn; 94*7c478bd9Sstevel@tonic-gate int error; 95*7c478bd9Sstevel@tonic-gate 96*7c478bd9Sstevel@tonic-gate error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf)); 97*7c478bd9Sstevel@tonic-gate if (error == 0) { 98*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 99*7c478bd9Sstevel@tonic-gate if (audit_active) 100*7c478bd9Sstevel@tonic-gate audit_lookupname(); 101*7c478bd9Sstevel@tonic-gate #endif 102*7c478bd9Sstevel@tonic-gate error = lookuppnat(&lookpn, NULL, followlink, 103*7c478bd9Sstevel@tonic-gate dirvpp, compvpp, startvp); 104*7c478bd9Sstevel@tonic-gate } 105*7c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG) { 106*7c478bd9Sstevel@tonic-gate /* 107*7c478bd9Sstevel@tonic-gate * This thread used a pathname > TYPICALMAXPATHLEN bytes long. 108*7c478bd9Sstevel@tonic-gate */ 109*7c478bd9Sstevel@tonic-gate if (error = pn_get(fnamep, seg, &lookpn)) 110*7c478bd9Sstevel@tonic-gate return (error); 111*7c478bd9Sstevel@tonic-gate error = lookuppnat(&lookpn, NULL, followlink, 112*7c478bd9Sstevel@tonic-gate dirvpp, compvpp, startvp); 113*7c478bd9Sstevel@tonic-gate pn_free(&lookpn); 114*7c478bd9Sstevel@tonic-gate } 115*7c478bd9Sstevel@tonic-gate 116*7c478bd9Sstevel@tonic-gate return (error); 117*7c478bd9Sstevel@tonic-gate } 118*7c478bd9Sstevel@tonic-gate 119*7c478bd9Sstevel@tonic-gate /* 120*7c478bd9Sstevel@tonic-gate * Lookup the user file name from a given vp, 121*7c478bd9Sstevel@tonic-gate */ 122*7c478bd9Sstevel@tonic-gate int 123*7c478bd9Sstevel@tonic-gate lookuppn( 124*7c478bd9Sstevel@tonic-gate struct pathname *pnp, 125*7c478bd9Sstevel@tonic-gate struct pathname *rpnp, 126*7c478bd9Sstevel@tonic-gate enum symfollow followlink, 127*7c478bd9Sstevel@tonic-gate vnode_t **dirvpp, 128*7c478bd9Sstevel@tonic-gate vnode_t **compvpp) 129*7c478bd9Sstevel@tonic-gate { 130*7c478bd9Sstevel@tonic-gate return (lookuppnat(pnp, rpnp, followlink, dirvpp, compvpp, NULL)); 131*7c478bd9Sstevel@tonic-gate } 132*7c478bd9Sstevel@tonic-gate 133*7c478bd9Sstevel@tonic-gate int 134*7c478bd9Sstevel@tonic-gate lookuppnat( 135*7c478bd9Sstevel@tonic-gate struct pathname *pnp, /* pathname to lookup */ 136*7c478bd9Sstevel@tonic-gate struct pathname *rpnp, /* if non-NULL, return resolved path */ 137*7c478bd9Sstevel@tonic-gate enum symfollow followlink, /* (don't) follow sym links */ 138*7c478bd9Sstevel@tonic-gate vnode_t **dirvpp, /* ptr for parent vnode */ 139*7c478bd9Sstevel@tonic-gate vnode_t **compvpp, /* ptr for entry vnode */ 140*7c478bd9Sstevel@tonic-gate vnode_t *startvp) /* start search from this vp */ 141*7c478bd9Sstevel@tonic-gate { 142*7c478bd9Sstevel@tonic-gate vnode_t *vp; /* current directory vp */ 143*7c478bd9Sstevel@tonic-gate vnode_t *rootvp; 144*7c478bd9Sstevel@tonic-gate proc_t *p = curproc; 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen == 0) 147*7c478bd9Sstevel@tonic-gate return (ENOENT); 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); /* for u_rdir and u_cdir */ 150*7c478bd9Sstevel@tonic-gate if ((rootvp = PTOU(p)->u_rdir) == NULL) 151*7c478bd9Sstevel@tonic-gate rootvp = rootdir; 152*7c478bd9Sstevel@tonic-gate else if (rootvp != rootdir) /* no need to VN_HOLD rootdir */ 153*7c478bd9Sstevel@tonic-gate VN_HOLD(rootvp); 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate if (pnp->pn_path[0] == '/') { 156*7c478bd9Sstevel@tonic-gate vp = rootvp; 157*7c478bd9Sstevel@tonic-gate } else { 158*7c478bd9Sstevel@tonic-gate vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp; 159*7c478bd9Sstevel@tonic-gate } 160*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 161*7c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 162*7c478bd9Sstevel@tonic-gate 163*7c478bd9Sstevel@tonic-gate /* 164*7c478bd9Sstevel@tonic-gate * Skip over leading slashes 165*7c478bd9Sstevel@tonic-gate */ 166*7c478bd9Sstevel@tonic-gate if (pnp->pn_path[0] == '/') { 167*7c478bd9Sstevel@tonic-gate do { 168*7c478bd9Sstevel@tonic-gate pnp->pn_path++; 169*7c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; 170*7c478bd9Sstevel@tonic-gate } while (pnp->pn_path[0] == '/'); 171*7c478bd9Sstevel@tonic-gate } 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate return (lookuppnvp(pnp, rpnp, followlink, dirvpp, 174*7c478bd9Sstevel@tonic-gate compvpp, rootvp, vp, CRED())); 175*7c478bd9Sstevel@tonic-gate } 176*7c478bd9Sstevel@tonic-gate 177*7c478bd9Sstevel@tonic-gate /* Private flag to do our getcwd() dirty work */ 178*7c478bd9Sstevel@tonic-gate #define LOOKUP_CHECKREAD 0x10 179*7c478bd9Sstevel@tonic-gate #define LOOKUP_MASK (~LOOKUP_CHECKREAD) 180*7c478bd9Sstevel@tonic-gate 181*7c478bd9Sstevel@tonic-gate /* 182*7c478bd9Sstevel@tonic-gate * Starting at current directory, translate pathname pnp to end. 183*7c478bd9Sstevel@tonic-gate * Leave pathname of final component in pnp, return the vnode 184*7c478bd9Sstevel@tonic-gate * for the final component in *compvpp, and return the vnode 185*7c478bd9Sstevel@tonic-gate * for the parent of the final component in dirvpp. 186*7c478bd9Sstevel@tonic-gate * 187*7c478bd9Sstevel@tonic-gate * This is the central routine in pathname translation and handles 188*7c478bd9Sstevel@tonic-gate * multiple components in pathnames, separating them at /'s. It also 189*7c478bd9Sstevel@tonic-gate * implements mounted file systems and processes symbolic links. 190*7c478bd9Sstevel@tonic-gate * 191*7c478bd9Sstevel@tonic-gate * vp is the vnode where the directory search should start. 192*7c478bd9Sstevel@tonic-gate * 193*7c478bd9Sstevel@tonic-gate * Reference counts: vp must be held prior to calling this function. rootvp 194*7c478bd9Sstevel@tonic-gate * should only be held if rootvp != rootdir. 195*7c478bd9Sstevel@tonic-gate */ 196*7c478bd9Sstevel@tonic-gate int 197*7c478bd9Sstevel@tonic-gate lookuppnvp( 198*7c478bd9Sstevel@tonic-gate struct pathname *pnp, /* pathname to lookup */ 199*7c478bd9Sstevel@tonic-gate struct pathname *rpnp, /* if non-NULL, return resolved path */ 200*7c478bd9Sstevel@tonic-gate int flags, /* follow symlinks */ 201*7c478bd9Sstevel@tonic-gate vnode_t **dirvpp, /* ptr for parent vnode */ 202*7c478bd9Sstevel@tonic-gate vnode_t **compvpp, /* ptr for entry vnode */ 203*7c478bd9Sstevel@tonic-gate vnode_t *rootvp, /* rootvp */ 204*7c478bd9Sstevel@tonic-gate vnode_t *vp, /* directory to start search at */ 205*7c478bd9Sstevel@tonic-gate cred_t *cr) /* user's credential */ 206*7c478bd9Sstevel@tonic-gate { 207*7c478bd9Sstevel@tonic-gate vnode_t *cvp; /* current component vp */ 208*7c478bd9Sstevel@tonic-gate vnode_t *tvp; /* addressable temp ptr */ 209*7c478bd9Sstevel@tonic-gate char component[MAXNAMELEN]; /* buffer for component (incl null) */ 210*7c478bd9Sstevel@tonic-gate int error; 211*7c478bd9Sstevel@tonic-gate int nlink; 212*7c478bd9Sstevel@tonic-gate int lookup_flags; 213*7c478bd9Sstevel@tonic-gate vnode_t *startvp; 214*7c478bd9Sstevel@tonic-gate vnode_t *zonevp = curproc->p_zone->zone_rootvp; /* zone root */ 215*7c478bd9Sstevel@tonic-gate int must_be_directory = 0; 216*7c478bd9Sstevel@tonic-gate size_t plen; 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate CPU_STATS_ADDQ(CPU, sys, namei, 1); 219*7c478bd9Sstevel@tonic-gate nlink = 0; 220*7c478bd9Sstevel@tonic-gate cvp = NULL; 221*7c478bd9Sstevel@tonic-gate if (rpnp) 222*7c478bd9Sstevel@tonic-gate rpnp->pn_pathlen = 0; 223*7c478bd9Sstevel@tonic-gate lookup_flags = dirvpp ? LOOKUP_DIR : 0; 224*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 225*7c478bd9Sstevel@tonic-gate if (audit_active) 226*7c478bd9Sstevel@tonic-gate audit_anchorpath(pnp, vp == rootvp); 227*7c478bd9Sstevel@tonic-gate #endif 228*7c478bd9Sstevel@tonic-gate 229*7c478bd9Sstevel@tonic-gate /* 230*7c478bd9Sstevel@tonic-gate * Eliminate any trailing slashes in the pathname. 231*7c478bd9Sstevel@tonic-gate * If there are any, we must follow all symlinks. 232*7c478bd9Sstevel@tonic-gate * Also, we must guarantee that the last component is a directory. 233*7c478bd9Sstevel@tonic-gate */ 234*7c478bd9Sstevel@tonic-gate if (pn_fixslash(pnp)) { 235*7c478bd9Sstevel@tonic-gate flags |= FOLLOW; 236*7c478bd9Sstevel@tonic-gate must_be_directory = 1; 237*7c478bd9Sstevel@tonic-gate } 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate startvp = vp; 240*7c478bd9Sstevel@tonic-gate next: 241*7c478bd9Sstevel@tonic-gate /* 242*7c478bd9Sstevel@tonic-gate * Make sure we have a directory. 243*7c478bd9Sstevel@tonic-gate */ 244*7c478bd9Sstevel@tonic-gate if (vp->v_type != VDIR) { 245*7c478bd9Sstevel@tonic-gate error = ENOTDIR; 246*7c478bd9Sstevel@tonic-gate goto bad; 247*7c478bd9Sstevel@tonic-gate } 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate if (rpnp && VN_CMP(vp, rootvp)) 250*7c478bd9Sstevel@tonic-gate (void) pn_set(rpnp, "/"); 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate /* 253*7c478bd9Sstevel@tonic-gate * Process the next component of the pathname. 254*7c478bd9Sstevel@tonic-gate */ 255*7c478bd9Sstevel@tonic-gate if (error = pn_getcomponent(pnp, component)) { 256*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 257*7c478bd9Sstevel@tonic-gate if (audit_active) 258*7c478bd9Sstevel@tonic-gate audit_addcomponent(pnp); 259*7c478bd9Sstevel@tonic-gate #endif 260*7c478bd9Sstevel@tonic-gate goto bad; 261*7c478bd9Sstevel@tonic-gate } 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate /* 264*7c478bd9Sstevel@tonic-gate * Handle "..": two special cases. 265*7c478bd9Sstevel@tonic-gate * 1. If we're at the root directory (e.g. after chroot or 266*7c478bd9Sstevel@tonic-gate * zone_enter) then change ".." to "." so we can't get 267*7c478bd9Sstevel@tonic-gate * out of this subtree. 268*7c478bd9Sstevel@tonic-gate * 2. If this vnode is the root of a mounted file system, 269*7c478bd9Sstevel@tonic-gate * then replace it with the vnode that was mounted on 270*7c478bd9Sstevel@tonic-gate * so that we take the ".." in the other file system. 271*7c478bd9Sstevel@tonic-gate */ 272*7c478bd9Sstevel@tonic-gate if (component[0] == '.' && component[1] == '.' && component[2] == 0) { 273*7c478bd9Sstevel@tonic-gate checkforroot: 274*7c478bd9Sstevel@tonic-gate if (VN_CMP(vp, rootvp) || VN_CMP(vp, zonevp)) { 275*7c478bd9Sstevel@tonic-gate component[1] = '\0'; 276*7c478bd9Sstevel@tonic-gate } else if (vp->v_flag & VROOT) { 277*7c478bd9Sstevel@tonic-gate vfs_t *vfsp; 278*7c478bd9Sstevel@tonic-gate cvp = vp; 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate /* 281*7c478bd9Sstevel@tonic-gate * While we deal with the vfs pointer from the vnode 282*7c478bd9Sstevel@tonic-gate * the filesystem could have been forcefully unmounted 283*7c478bd9Sstevel@tonic-gate * and the vnode's v_vfsp could have been invalidated 284*7c478bd9Sstevel@tonic-gate * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it 285*7c478bd9Sstevel@tonic-gate * with vfs_rlock_wait/vfs_unlock. 286*7c478bd9Sstevel@tonic-gate * It is safe to use the v_vfsp even it is freed by 287*7c478bd9Sstevel@tonic-gate * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock 288*7c478bd9Sstevel@tonic-gate * do not dereference v_vfsp. It is just used as a 289*7c478bd9Sstevel@tonic-gate * magic cookie. 290*7c478bd9Sstevel@tonic-gate * One more corner case here is the memory getting 291*7c478bd9Sstevel@tonic-gate * reused for another vfs structure. In this case 292*7c478bd9Sstevel@tonic-gate * lookuppnvp's vfs_rlock_wait will succeed, domount's 293*7c478bd9Sstevel@tonic-gate * vfs_lock will fail and domount will bail out with an 294*7c478bd9Sstevel@tonic-gate * error (EBUSY). 295*7c478bd9Sstevel@tonic-gate */ 296*7c478bd9Sstevel@tonic-gate vfsp = cvp->v_vfsp; 297*7c478bd9Sstevel@tonic-gate 298*7c478bd9Sstevel@tonic-gate /* 299*7c478bd9Sstevel@tonic-gate * This lock is used to synchronize 300*7c478bd9Sstevel@tonic-gate * mounts/unmounts and lookups. 301*7c478bd9Sstevel@tonic-gate * Threads doing mounts/unmounts hold the 302*7c478bd9Sstevel@tonic-gate * writers version vfs_lock_wait(). 303*7c478bd9Sstevel@tonic-gate */ 304*7c478bd9Sstevel@tonic-gate 305*7c478bd9Sstevel@tonic-gate vfs_rlock_wait(vfsp); 306*7c478bd9Sstevel@tonic-gate 307*7c478bd9Sstevel@tonic-gate /* 308*7c478bd9Sstevel@tonic-gate * If this vnode is on a file system that 309*7c478bd9Sstevel@tonic-gate * has been forcibly unmounted, 310*7c478bd9Sstevel@tonic-gate * we can't proceed. Cancel this operation 311*7c478bd9Sstevel@tonic-gate * and return EIO. 312*7c478bd9Sstevel@tonic-gate * 313*7c478bd9Sstevel@tonic-gate * vfs_vnodecovered is NULL if unmounted. 314*7c478bd9Sstevel@tonic-gate * Currently, nfs uses VFS_UNMOUNTED to 315*7c478bd9Sstevel@tonic-gate * check if it's a forced-umount. Keep the 316*7c478bd9Sstevel@tonic-gate * same checking here as well even though it 317*7c478bd9Sstevel@tonic-gate * may not be needed. 318*7c478bd9Sstevel@tonic-gate */ 319*7c478bd9Sstevel@tonic-gate if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || 320*7c478bd9Sstevel@tonic-gate (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 321*7c478bd9Sstevel@tonic-gate vfs_unlock(vfsp); 322*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 323*7c478bd9Sstevel@tonic-gate return (EIO); 324*7c478bd9Sstevel@tonic-gate } 325*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 326*7c478bd9Sstevel@tonic-gate vfs_unlock(vfsp); 327*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 328*7c478bd9Sstevel@tonic-gate cvp = NULL; 329*7c478bd9Sstevel@tonic-gate goto checkforroot; 330*7c478bd9Sstevel@tonic-gate } 331*7c478bd9Sstevel@tonic-gate } 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate /* 334*7c478bd9Sstevel@tonic-gate * LOOKUP_CHECKREAD is a private flag used by vnodetopath() to indicate 335*7c478bd9Sstevel@tonic-gate * that we need to have read permission on every directory in the entire 336*7c478bd9Sstevel@tonic-gate * path. This is used to ensure that a forward-lookup of a cached value 337*7c478bd9Sstevel@tonic-gate * has the same effect as a reverse-lookup when the cached value cannot 338*7c478bd9Sstevel@tonic-gate * be found. 339*7c478bd9Sstevel@tonic-gate */ 340*7c478bd9Sstevel@tonic-gate if ((flags & LOOKUP_CHECKREAD) && 341*7c478bd9Sstevel@tonic-gate (error = VOP_ACCESS(vp, VREAD, 0, cr)) != 0) 342*7c478bd9Sstevel@tonic-gate goto bad; 343*7c478bd9Sstevel@tonic-gate 344*7c478bd9Sstevel@tonic-gate /* 345*7c478bd9Sstevel@tonic-gate * Perform a lookup in the current directory. 346*7c478bd9Sstevel@tonic-gate */ 347*7c478bd9Sstevel@tonic-gate error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, 348*7c478bd9Sstevel@tonic-gate rootvp, cr); 349*7c478bd9Sstevel@tonic-gate cvp = tvp; 350*7c478bd9Sstevel@tonic-gate if (error) { 351*7c478bd9Sstevel@tonic-gate cvp = NULL; 352*7c478bd9Sstevel@tonic-gate /* 353*7c478bd9Sstevel@tonic-gate * On error, return hard error if 354*7c478bd9Sstevel@tonic-gate * (a) we're not at the end of the pathname yet, or 355*7c478bd9Sstevel@tonic-gate * (b) the caller didn't want the parent directory, or 356*7c478bd9Sstevel@tonic-gate * (c) we failed for some reason other than a missing entry. 357*7c478bd9Sstevel@tonic-gate */ 358*7c478bd9Sstevel@tonic-gate if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT) 359*7c478bd9Sstevel@tonic-gate goto bad; 360*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 361*7c478bd9Sstevel@tonic-gate if (audit_active) { /* directory access */ 362*7c478bd9Sstevel@tonic-gate if (error = audit_savepath(pnp, vp, error, cr)) 363*7c478bd9Sstevel@tonic-gate goto bad_noaudit; 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate #endif 366*7c478bd9Sstevel@tonic-gate pn_setlast(pnp); 367*7c478bd9Sstevel@tonic-gate /* 368*7c478bd9Sstevel@tonic-gate * We inform the caller that the desired entry must be 369*7c478bd9Sstevel@tonic-gate * a directory by adding a '/' to the component name. 370*7c478bd9Sstevel@tonic-gate */ 371*7c478bd9Sstevel@tonic-gate if (must_be_directory && (error = pn_addslash(pnp)) != 0) 372*7c478bd9Sstevel@tonic-gate goto bad; 373*7c478bd9Sstevel@tonic-gate *dirvpp = vp; 374*7c478bd9Sstevel@tonic-gate /* 375*7c478bd9Sstevel@tonic-gate * We cache the path of everything up to right before this 376*7c478bd9Sstevel@tonic-gate * component and store that in the parent directory. 377*7c478bd9Sstevel@tonic-gate */ 378*7c478bd9Sstevel@tonic-gate if (vfs_vnode_path && pnp->pn_path != pnp->pn_buf) { 379*7c478bd9Sstevel@tonic-gate VN_SETPATH(rootvp, startvp, vp, pnp->pn_buf, 380*7c478bd9Sstevel@tonic-gate pnp->pn_path - pnp->pn_buf); 381*7c478bd9Sstevel@tonic-gate } 382*7c478bd9Sstevel@tonic-gate if (compvpp != NULL) 383*7c478bd9Sstevel@tonic-gate *compvpp = NULL; 384*7c478bd9Sstevel@tonic-gate if (rootvp != rootdir) 385*7c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 386*7c478bd9Sstevel@tonic-gate return (0); 387*7c478bd9Sstevel@tonic-gate } 388*7c478bd9Sstevel@tonic-gate 389*7c478bd9Sstevel@tonic-gate /* 390*7c478bd9Sstevel@tonic-gate * Traverse mount points. 391*7c478bd9Sstevel@tonic-gate * XXX why don't we need to hold a read lock here (call vn_vfsrlock)? 392*7c478bd9Sstevel@tonic-gate * What prevents a concurrent update to v_vfsmountedhere? 393*7c478bd9Sstevel@tonic-gate * Possible answer: if mounting, we might not see the mount 394*7c478bd9Sstevel@tonic-gate * if it is concurrently coming into existence, but that's 395*7c478bd9Sstevel@tonic-gate * really not much different from the thread running a bit slower. 396*7c478bd9Sstevel@tonic-gate * If unmounting, we may get into traverse() when we shouldn't, 397*7c478bd9Sstevel@tonic-gate * but traverse() will catch this case for us. 398*7c478bd9Sstevel@tonic-gate * (For this to work, fetching v_vfsmountedhere had better 399*7c478bd9Sstevel@tonic-gate * be atomic!) 400*7c478bd9Sstevel@tonic-gate */ 401*7c478bd9Sstevel@tonic-gate if (vn_mountedvfs(cvp) != NULL) { 402*7c478bd9Sstevel@tonic-gate tvp = cvp; 403*7c478bd9Sstevel@tonic-gate if ((error = traverse(&tvp)) != 0) { 404*7c478bd9Sstevel@tonic-gate /* 405*7c478bd9Sstevel@tonic-gate * It is required to assign cvp here, because 406*7c478bd9Sstevel@tonic-gate * traverse() will return a held vnode which 407*7c478bd9Sstevel@tonic-gate * may different than the vnode that was passed 408*7c478bd9Sstevel@tonic-gate * in (even in the error case). If traverse() 409*7c478bd9Sstevel@tonic-gate * changes the vnode it releases the original, 410*7c478bd9Sstevel@tonic-gate * and holds the new one. 411*7c478bd9Sstevel@tonic-gate */ 412*7c478bd9Sstevel@tonic-gate cvp = tvp; 413*7c478bd9Sstevel@tonic-gate goto bad; 414*7c478bd9Sstevel@tonic-gate } 415*7c478bd9Sstevel@tonic-gate cvp = tvp; 416*7c478bd9Sstevel@tonic-gate } 417*7c478bd9Sstevel@tonic-gate 418*7c478bd9Sstevel@tonic-gate /* 419*7c478bd9Sstevel@tonic-gate * If we hit a symbolic link and there is more path to be 420*7c478bd9Sstevel@tonic-gate * translated or this operation does not wish to apply 421*7c478bd9Sstevel@tonic-gate * to a link, then place the contents of the link at the 422*7c478bd9Sstevel@tonic-gate * front of the remaining pathname. 423*7c478bd9Sstevel@tonic-gate */ 424*7c478bd9Sstevel@tonic-gate if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) { 425*7c478bd9Sstevel@tonic-gate struct pathname linkpath; 426*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 427*7c478bd9Sstevel@tonic-gate if (audit_active) { 428*7c478bd9Sstevel@tonic-gate if (error = audit_pathcomp(pnp, cvp, cr)) 429*7c478bd9Sstevel@tonic-gate goto bad; 430*7c478bd9Sstevel@tonic-gate } 431*7c478bd9Sstevel@tonic-gate #endif 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate if (++nlink > MAXSYMLINKS) { 434*7c478bd9Sstevel@tonic-gate error = ELOOP; 435*7c478bd9Sstevel@tonic-gate goto bad; 436*7c478bd9Sstevel@tonic-gate } 437*7c478bd9Sstevel@tonic-gate pn_alloc(&linkpath); 438*7c478bd9Sstevel@tonic-gate if (error = pn_getsymlink(cvp, &linkpath, cr)) { 439*7c478bd9Sstevel@tonic-gate pn_free(&linkpath); 440*7c478bd9Sstevel@tonic-gate goto bad; 441*7c478bd9Sstevel@tonic-gate } 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 444*7c478bd9Sstevel@tonic-gate if (audit_active) 445*7c478bd9Sstevel@tonic-gate audit_symlink(pnp, &linkpath); 446*7c478bd9Sstevel@tonic-gate #endif /* C2_AUDIT */ 447*7c478bd9Sstevel@tonic-gate 448*7c478bd9Sstevel@tonic-gate if (pn_pathleft(&linkpath) == 0) 449*7c478bd9Sstevel@tonic-gate (void) pn_set(&linkpath, "."); 450*7c478bd9Sstevel@tonic-gate error = pn_insert(pnp, &linkpath, strlen(component)); 451*7c478bd9Sstevel@tonic-gate pn_free(&linkpath); 452*7c478bd9Sstevel@tonic-gate if (error) 453*7c478bd9Sstevel@tonic-gate goto bad; 454*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 455*7c478bd9Sstevel@tonic-gate cvp = NULL; 456*7c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen == 0) { 457*7c478bd9Sstevel@tonic-gate error = ENOENT; 458*7c478bd9Sstevel@tonic-gate goto bad; 459*7c478bd9Sstevel@tonic-gate } 460*7c478bd9Sstevel@tonic-gate if (pnp->pn_path[0] == '/') { 461*7c478bd9Sstevel@tonic-gate do { 462*7c478bd9Sstevel@tonic-gate pnp->pn_path++; 463*7c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; 464*7c478bd9Sstevel@tonic-gate } while (pnp->pn_path[0] == '/'); 465*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 466*7c478bd9Sstevel@tonic-gate vp = rootvp; 467*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 468*7c478bd9Sstevel@tonic-gate } 469*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 470*7c478bd9Sstevel@tonic-gate if (audit_active) 471*7c478bd9Sstevel@tonic-gate audit_anchorpath(pnp, vp == rootvp); 472*7c478bd9Sstevel@tonic-gate #endif 473*7c478bd9Sstevel@tonic-gate if (pn_fixslash(pnp)) { 474*7c478bd9Sstevel@tonic-gate flags |= FOLLOW; 475*7c478bd9Sstevel@tonic-gate must_be_directory = 1; 476*7c478bd9Sstevel@tonic-gate } 477*7c478bd9Sstevel@tonic-gate goto next; 478*7c478bd9Sstevel@tonic-gate } 479*7c478bd9Sstevel@tonic-gate 480*7c478bd9Sstevel@tonic-gate /* 481*7c478bd9Sstevel@tonic-gate * If rpnp is non-NULL, remember the resolved path name therein. 482*7c478bd9Sstevel@tonic-gate * Do not include "." components. Collapse occurrences of 483*7c478bd9Sstevel@tonic-gate * "previous/..", so long as "previous" is not itself "..". 484*7c478bd9Sstevel@tonic-gate * Exhausting rpnp results in error ENAMETOOLONG. 485*7c478bd9Sstevel@tonic-gate */ 486*7c478bd9Sstevel@tonic-gate if (rpnp && strcmp(component, ".") != 0) { 487*7c478bd9Sstevel@tonic-gate size_t len; 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate if (strcmp(component, "..") == 0 && 490*7c478bd9Sstevel@tonic-gate rpnp->pn_pathlen != 0 && 491*7c478bd9Sstevel@tonic-gate !((rpnp->pn_pathlen > 2 && 492*7c478bd9Sstevel@tonic-gate strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) || 493*7c478bd9Sstevel@tonic-gate (rpnp->pn_pathlen == 2 && 494*7c478bd9Sstevel@tonic-gate strncmp(rpnp->pn_path, "..", 2) == 0))) { 495*7c478bd9Sstevel@tonic-gate while (rpnp->pn_pathlen && 496*7c478bd9Sstevel@tonic-gate rpnp->pn_path[rpnp->pn_pathlen-1] != '/') 497*7c478bd9Sstevel@tonic-gate rpnp->pn_pathlen--; 498*7c478bd9Sstevel@tonic-gate if (rpnp->pn_pathlen > 1) 499*7c478bd9Sstevel@tonic-gate rpnp->pn_pathlen--; 500*7c478bd9Sstevel@tonic-gate rpnp->pn_path[rpnp->pn_pathlen] = '\0'; 501*7c478bd9Sstevel@tonic-gate } else { 502*7c478bd9Sstevel@tonic-gate if (rpnp->pn_pathlen != 0 && 503*7c478bd9Sstevel@tonic-gate rpnp->pn_path[rpnp->pn_pathlen-1] != '/') 504*7c478bd9Sstevel@tonic-gate rpnp->pn_path[rpnp->pn_pathlen++] = '/'; 505*7c478bd9Sstevel@tonic-gate error = copystr(component, 506*7c478bd9Sstevel@tonic-gate rpnp->pn_path + rpnp->pn_pathlen, 507*7c478bd9Sstevel@tonic-gate rpnp->pn_bufsize - rpnp->pn_pathlen, &len); 508*7c478bd9Sstevel@tonic-gate if (error) /* copystr() returns ENAMETOOLONG */ 509*7c478bd9Sstevel@tonic-gate goto bad; 510*7c478bd9Sstevel@tonic-gate rpnp->pn_pathlen += (len - 1); 511*7c478bd9Sstevel@tonic-gate ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen); 512*7c478bd9Sstevel@tonic-gate } 513*7c478bd9Sstevel@tonic-gate } 514*7c478bd9Sstevel@tonic-gate 515*7c478bd9Sstevel@tonic-gate /* 516*7c478bd9Sstevel@tonic-gate * If no more components, return last directory (if wanted) and 517*7c478bd9Sstevel@tonic-gate * last component (if wanted). 518*7c478bd9Sstevel@tonic-gate */ 519*7c478bd9Sstevel@tonic-gate if (pn_pathleft(pnp) == 0) { 520*7c478bd9Sstevel@tonic-gate /* 521*7c478bd9Sstevel@tonic-gate * If there was a trailing slash in the pathname, 522*7c478bd9Sstevel@tonic-gate * make sure the last component is a directory. 523*7c478bd9Sstevel@tonic-gate */ 524*7c478bd9Sstevel@tonic-gate if (must_be_directory && cvp->v_type != VDIR) { 525*7c478bd9Sstevel@tonic-gate error = ENOTDIR; 526*7c478bd9Sstevel@tonic-gate goto bad; 527*7c478bd9Sstevel@tonic-gate } 528*7c478bd9Sstevel@tonic-gate if (dirvpp != NULL) { 529*7c478bd9Sstevel@tonic-gate /* 530*7c478bd9Sstevel@tonic-gate * Check that we have the real parent and not 531*7c478bd9Sstevel@tonic-gate * an alias of the last component. 532*7c478bd9Sstevel@tonic-gate */ 533*7c478bd9Sstevel@tonic-gate if (vn_compare(vp, cvp)) { 534*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 535*7c478bd9Sstevel@tonic-gate if (audit_active) 536*7c478bd9Sstevel@tonic-gate (void) audit_savepath(pnp, cvp, 537*7c478bd9Sstevel@tonic-gate EINVAL, cr); 538*7c478bd9Sstevel@tonic-gate #endif 539*7c478bd9Sstevel@tonic-gate pn_setlast(pnp); 540*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 541*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 542*7c478bd9Sstevel@tonic-gate if (rootvp != rootdir) 543*7c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 544*7c478bd9Sstevel@tonic-gate return (EINVAL); 545*7c478bd9Sstevel@tonic-gate } 546*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 547*7c478bd9Sstevel@tonic-gate if (audit_active) { 548*7c478bd9Sstevel@tonic-gate if (error = audit_pathcomp(pnp, vp, cr)) 549*7c478bd9Sstevel@tonic-gate goto bad; 550*7c478bd9Sstevel@tonic-gate } 551*7c478bd9Sstevel@tonic-gate #endif 552*7c478bd9Sstevel@tonic-gate *dirvpp = vp; 553*7c478bd9Sstevel@tonic-gate } else 554*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 555*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 556*7c478bd9Sstevel@tonic-gate if (audit_active) 557*7c478bd9Sstevel@tonic-gate (void) audit_savepath(pnp, cvp, 0, cr); 558*7c478bd9Sstevel@tonic-gate #endif 559*7c478bd9Sstevel@tonic-gate if (pnp->pn_path == pnp->pn_buf) 560*7c478bd9Sstevel@tonic-gate (void) pn_set(pnp, "."); 561*7c478bd9Sstevel@tonic-gate else 562*7c478bd9Sstevel@tonic-gate pn_setlast(pnp); 563*7c478bd9Sstevel@tonic-gate if (rpnp) { 564*7c478bd9Sstevel@tonic-gate if (VN_CMP(cvp, rootvp)) 565*7c478bd9Sstevel@tonic-gate (void) pn_set(rpnp, "/"); 566*7c478bd9Sstevel@tonic-gate else if (rpnp->pn_pathlen == 0) 567*7c478bd9Sstevel@tonic-gate (void) pn_set(rpnp, "."); 568*7c478bd9Sstevel@tonic-gate } 569*7c478bd9Sstevel@tonic-gate 570*7c478bd9Sstevel@tonic-gate /* 571*7c478bd9Sstevel@tonic-gate * Store the path for this vnode and/or its parent. 572*7c478bd9Sstevel@tonic-gate */ 573*7c478bd9Sstevel@tonic-gate if (vfs_vnode_path) { 574*7c478bd9Sstevel@tonic-gate plen = pnp->pn_path - pnp->pn_buf; 575*7c478bd9Sstevel@tonic-gate if (dirvpp != NULL && plen != 0) 576*7c478bd9Sstevel@tonic-gate VN_SETPATH(rootvp, startvp, *dirvpp, 577*7c478bd9Sstevel@tonic-gate pnp->pn_buf, plen); 578*7c478bd9Sstevel@tonic-gate VN_SETPATH(rootvp, startvp, cvp, pnp->pn_buf, 579*7c478bd9Sstevel@tonic-gate plen + pnp->pn_pathlen); 580*7c478bd9Sstevel@tonic-gate } 581*7c478bd9Sstevel@tonic-gate 582*7c478bd9Sstevel@tonic-gate if (compvpp != NULL) 583*7c478bd9Sstevel@tonic-gate *compvpp = cvp; 584*7c478bd9Sstevel@tonic-gate else 585*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 586*7c478bd9Sstevel@tonic-gate if (rootvp != rootdir) 587*7c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 588*7c478bd9Sstevel@tonic-gate return (0); 589*7c478bd9Sstevel@tonic-gate } 590*7c478bd9Sstevel@tonic-gate 591*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 592*7c478bd9Sstevel@tonic-gate if (audit_active) { 593*7c478bd9Sstevel@tonic-gate if (error = audit_pathcomp(pnp, cvp, cr)) 594*7c478bd9Sstevel@tonic-gate goto bad; 595*7c478bd9Sstevel@tonic-gate } 596*7c478bd9Sstevel@tonic-gate #endif 597*7c478bd9Sstevel@tonic-gate 598*7c478bd9Sstevel@tonic-gate /* 599*7c478bd9Sstevel@tonic-gate * Skip over slashes from end of last component. 600*7c478bd9Sstevel@tonic-gate */ 601*7c478bd9Sstevel@tonic-gate while (pnp->pn_path[0] == '/') { 602*7c478bd9Sstevel@tonic-gate pnp->pn_path++; 603*7c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; 604*7c478bd9Sstevel@tonic-gate } 605*7c478bd9Sstevel@tonic-gate 606*7c478bd9Sstevel@tonic-gate /* 607*7c478bd9Sstevel@tonic-gate * Searched through another level of directory: 608*7c478bd9Sstevel@tonic-gate * release previous directory handle and save new (result 609*7c478bd9Sstevel@tonic-gate * of lookup) as current directory. 610*7c478bd9Sstevel@tonic-gate */ 611*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 612*7c478bd9Sstevel@tonic-gate vp = cvp; 613*7c478bd9Sstevel@tonic-gate cvp = NULL; 614*7c478bd9Sstevel@tonic-gate goto next; 615*7c478bd9Sstevel@tonic-gate 616*7c478bd9Sstevel@tonic-gate bad: 617*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT 618*7c478bd9Sstevel@tonic-gate if (audit_active) /* reached end of path */ 619*7c478bd9Sstevel@tonic-gate (void) audit_savepath(pnp, cvp, error, cr); 620*7c478bd9Sstevel@tonic-gate bad_noaudit: 621*7c478bd9Sstevel@tonic-gate #endif 622*7c478bd9Sstevel@tonic-gate /* 623*7c478bd9Sstevel@tonic-gate * Error. Release vnodes and return. 624*7c478bd9Sstevel@tonic-gate */ 625*7c478bd9Sstevel@tonic-gate if (cvp) 626*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 627*7c478bd9Sstevel@tonic-gate /* 628*7c478bd9Sstevel@tonic-gate * If the error was ESTALE and the current directory to look in 629*7c478bd9Sstevel@tonic-gate * was the root for this lookup, the root for a mounted file 630*7c478bd9Sstevel@tonic-gate * system, or the starting directory for lookups, then 631*7c478bd9Sstevel@tonic-gate * return ENOENT instead of ESTALE. In this case, no recovery 632*7c478bd9Sstevel@tonic-gate * is possible by the higher level. If ESTALE was returned for 633*7c478bd9Sstevel@tonic-gate * some intermediate directory along the path, then recovery 634*7c478bd9Sstevel@tonic-gate * is potentially possible and retrying from the higher level 635*7c478bd9Sstevel@tonic-gate * will either correct the situation by purging stale cache 636*7c478bd9Sstevel@tonic-gate * entries or eventually get back to the point where no recovery 637*7c478bd9Sstevel@tonic-gate * is possible. 638*7c478bd9Sstevel@tonic-gate */ 639*7c478bd9Sstevel@tonic-gate if (error == ESTALE && 640*7c478bd9Sstevel@tonic-gate (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp)) 641*7c478bd9Sstevel@tonic-gate error = ENOENT; 642*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 643*7c478bd9Sstevel@tonic-gate if (rootvp != rootdir) 644*7c478bd9Sstevel@tonic-gate VN_RELE(rootvp); 645*7c478bd9Sstevel@tonic-gate return (error); 646*7c478bd9Sstevel@tonic-gate } 647*7c478bd9Sstevel@tonic-gate 648*7c478bd9Sstevel@tonic-gate /* 649*7c478bd9Sstevel@tonic-gate * Traverse a mount point. Routine accepts a vnode pointer as a reference 650*7c478bd9Sstevel@tonic-gate * parameter and performs the indirection, releasing the original vnode. 651*7c478bd9Sstevel@tonic-gate */ 652*7c478bd9Sstevel@tonic-gate int 653*7c478bd9Sstevel@tonic-gate traverse(vnode_t **cvpp) 654*7c478bd9Sstevel@tonic-gate { 655*7c478bd9Sstevel@tonic-gate int error = 0; 656*7c478bd9Sstevel@tonic-gate vnode_t *cvp; 657*7c478bd9Sstevel@tonic-gate vnode_t *tvp; 658*7c478bd9Sstevel@tonic-gate vfs_t *vfsp; 659*7c478bd9Sstevel@tonic-gate 660*7c478bd9Sstevel@tonic-gate cvp = *cvpp; 661*7c478bd9Sstevel@tonic-gate 662*7c478bd9Sstevel@tonic-gate /* 663*7c478bd9Sstevel@tonic-gate * If this vnode is mounted on, then we transparently indirect 664*7c478bd9Sstevel@tonic-gate * to the vnode which is the root of the mounted file system. 665*7c478bd9Sstevel@tonic-gate * Before we do this we must check that an unmount is not in 666*7c478bd9Sstevel@tonic-gate * progress on this vnode. 667*7c478bd9Sstevel@tonic-gate */ 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate for (;;) { 670*7c478bd9Sstevel@tonic-gate /* 671*7c478bd9Sstevel@tonic-gate * Try to read lock the vnode. If this fails because 672*7c478bd9Sstevel@tonic-gate * the vnode is already write locked, then check to 673*7c478bd9Sstevel@tonic-gate * see whether it is the current thread which locked 674*7c478bd9Sstevel@tonic-gate * the vnode. If it is not, then read lock the vnode 675*7c478bd9Sstevel@tonic-gate * by waiting to acquire the lock. 676*7c478bd9Sstevel@tonic-gate * 677*7c478bd9Sstevel@tonic-gate * The code path in domount() is an example of support 678*7c478bd9Sstevel@tonic-gate * which needs to look up two pathnames and locks one 679*7c478bd9Sstevel@tonic-gate * of them in between the two lookups. 680*7c478bd9Sstevel@tonic-gate */ 681*7c478bd9Sstevel@tonic-gate error = vn_vfsrlock(cvp); 682*7c478bd9Sstevel@tonic-gate if (error) { 683*7c478bd9Sstevel@tonic-gate if (!vn_vfswlock_held(cvp)) 684*7c478bd9Sstevel@tonic-gate error = vn_vfsrlock_wait(cvp); 685*7c478bd9Sstevel@tonic-gate if (error != 0) { 686*7c478bd9Sstevel@tonic-gate /* 687*7c478bd9Sstevel@tonic-gate * lookuppn() expects a held vnode to be 688*7c478bd9Sstevel@tonic-gate * returned because it promptly calls 689*7c478bd9Sstevel@tonic-gate * VN_RELE after the error return 690*7c478bd9Sstevel@tonic-gate */ 691*7c478bd9Sstevel@tonic-gate *cvpp = cvp; 692*7c478bd9Sstevel@tonic-gate return (error); 693*7c478bd9Sstevel@tonic-gate } 694*7c478bd9Sstevel@tonic-gate } 695*7c478bd9Sstevel@tonic-gate 696*7c478bd9Sstevel@tonic-gate /* 697*7c478bd9Sstevel@tonic-gate * Reached the end of the mount chain? 698*7c478bd9Sstevel@tonic-gate */ 699*7c478bd9Sstevel@tonic-gate vfsp = vn_mountedvfs(cvp); 700*7c478bd9Sstevel@tonic-gate if (vfsp == NULL) { 701*7c478bd9Sstevel@tonic-gate vn_vfsunlock(cvp); 702*7c478bd9Sstevel@tonic-gate break; 703*7c478bd9Sstevel@tonic-gate } 704*7c478bd9Sstevel@tonic-gate 705*7c478bd9Sstevel@tonic-gate /* 706*7c478bd9Sstevel@tonic-gate * The read lock must be held across the call to VFS_ROOT() to 707*7c478bd9Sstevel@tonic-gate * prevent a concurrent unmount from destroying the vfs. 708*7c478bd9Sstevel@tonic-gate */ 709*7c478bd9Sstevel@tonic-gate error = VFS_ROOT(vfsp, &tvp); 710*7c478bd9Sstevel@tonic-gate vn_vfsunlock(cvp); 711*7c478bd9Sstevel@tonic-gate 712*7c478bd9Sstevel@tonic-gate if (error) 713*7c478bd9Sstevel@tonic-gate break; 714*7c478bd9Sstevel@tonic-gate 715*7c478bd9Sstevel@tonic-gate VN_RELE(cvp); 716*7c478bd9Sstevel@tonic-gate 717*7c478bd9Sstevel@tonic-gate cvp = tvp; 718*7c478bd9Sstevel@tonic-gate } 719*7c478bd9Sstevel@tonic-gate 720*7c478bd9Sstevel@tonic-gate *cvpp = cvp; 721*7c478bd9Sstevel@tonic-gate return (error); 722*7c478bd9Sstevel@tonic-gate } 723*7c478bd9Sstevel@tonic-gate 724*7c478bd9Sstevel@tonic-gate /* 725*7c478bd9Sstevel@tonic-gate * Return the lowermost vnode if this is a mountpoint. 726*7c478bd9Sstevel@tonic-gate */ 727*7c478bd9Sstevel@tonic-gate static vnode_t * 728*7c478bd9Sstevel@tonic-gate vn_under(vnode_t *vp) 729*7c478bd9Sstevel@tonic-gate { 730*7c478bd9Sstevel@tonic-gate vnode_t *uvp; 731*7c478bd9Sstevel@tonic-gate vfs_t *vfsp; 732*7c478bd9Sstevel@tonic-gate 733*7c478bd9Sstevel@tonic-gate while (vp->v_flag & VROOT) { 734*7c478bd9Sstevel@tonic-gate 735*7c478bd9Sstevel@tonic-gate vfsp = vp->v_vfsp; 736*7c478bd9Sstevel@tonic-gate vfs_rlock_wait(vfsp); 737*7c478bd9Sstevel@tonic-gate if ((uvp = vfsp->vfs_vnodecovered) == NULL || 738*7c478bd9Sstevel@tonic-gate (vfsp->vfs_flag & VFS_UNMOUNTED)) { 739*7c478bd9Sstevel@tonic-gate vfs_unlock(vfsp); 740*7c478bd9Sstevel@tonic-gate break; 741*7c478bd9Sstevel@tonic-gate } 742*7c478bd9Sstevel@tonic-gate VN_HOLD(uvp); 743*7c478bd9Sstevel@tonic-gate vfs_unlock(vfsp); 744*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 745*7c478bd9Sstevel@tonic-gate vp = uvp; 746*7c478bd9Sstevel@tonic-gate } 747*7c478bd9Sstevel@tonic-gate 748*7c478bd9Sstevel@tonic-gate return (vp); 749*7c478bd9Sstevel@tonic-gate } 750*7c478bd9Sstevel@tonic-gate 751*7c478bd9Sstevel@tonic-gate static int 752*7c478bd9Sstevel@tonic-gate vnode_match(vnode_t *v1, vnode_t *v2, cred_t *cr) 753*7c478bd9Sstevel@tonic-gate { 754*7c478bd9Sstevel@tonic-gate vattr_t v1attr, v2attr; 755*7c478bd9Sstevel@tonic-gate 756*7c478bd9Sstevel@tonic-gate /* 757*7c478bd9Sstevel@tonic-gate * If we have a device file, check to see if is a cloned open of the 758*7c478bd9Sstevel@tonic-gate * same device. For self-cloning devices, the major numbers will match. 759*7c478bd9Sstevel@tonic-gate * For devices cloned through the 'clone' driver, the minor number of 760*7c478bd9Sstevel@tonic-gate * the source device will be the same as the major number of the cloned 761*7c478bd9Sstevel@tonic-gate * device. 762*7c478bd9Sstevel@tonic-gate */ 763*7c478bd9Sstevel@tonic-gate if ((v1->v_type == VCHR || v1->v_type == VBLK) && 764*7c478bd9Sstevel@tonic-gate v1->v_type == v2->v_type) { 765*7c478bd9Sstevel@tonic-gate if ((spec_is_selfclone(v1) || spec_is_selfclone(v2)) && 766*7c478bd9Sstevel@tonic-gate getmajor(v1->v_rdev) == getmajor(v2->v_rdev)) 767*7c478bd9Sstevel@tonic-gate return (1); 768*7c478bd9Sstevel@tonic-gate 769*7c478bd9Sstevel@tonic-gate if (spec_is_clone(v1) && 770*7c478bd9Sstevel@tonic-gate getmajor(v1->v_rdev) == getminor(v2->v_rdev)) 771*7c478bd9Sstevel@tonic-gate return (1); 772*7c478bd9Sstevel@tonic-gate 773*7c478bd9Sstevel@tonic-gate if (spec_is_clone(v2) && 774*7c478bd9Sstevel@tonic-gate getmajor(v2->v_rdev) == getminor(v1->v_rdev)) 775*7c478bd9Sstevel@tonic-gate return (1); 776*7c478bd9Sstevel@tonic-gate } 777*7c478bd9Sstevel@tonic-gate 778*7c478bd9Sstevel@tonic-gate v1attr.va_mask = v2attr.va_mask = AT_TYPE; 779*7c478bd9Sstevel@tonic-gate 780*7c478bd9Sstevel@tonic-gate /* 781*7c478bd9Sstevel@tonic-gate * This check for symbolic links handles the pseudo-symlinks in procfs. 782*7c478bd9Sstevel@tonic-gate * These particular links have v_type of VDIR, but the attributes have a 783*7c478bd9Sstevel@tonic-gate * type of VLNK. We need to avoid these links because otherwise if we 784*7c478bd9Sstevel@tonic-gate * are currently in '/proc/self/fd', then '/proc/self/cwd' will compare 785*7c478bd9Sstevel@tonic-gate * as the same vnode. 786*7c478bd9Sstevel@tonic-gate */ 787*7c478bd9Sstevel@tonic-gate if (VOP_GETATTR(v1, &v1attr, 0, cr) != 0 || 788*7c478bd9Sstevel@tonic-gate VOP_GETATTR(v2, &v2attr, 0, cr) != 0 || 789*7c478bd9Sstevel@tonic-gate v1attr.va_type == VLNK || v2attr.va_type == VLNK) 790*7c478bd9Sstevel@tonic-gate return (0); 791*7c478bd9Sstevel@tonic-gate 792*7c478bd9Sstevel@tonic-gate v1attr.va_mask = v2attr.va_mask = AT_TYPE | AT_FSID | AT_NODEID; 793*7c478bd9Sstevel@tonic-gate 794*7c478bd9Sstevel@tonic-gate if (VOP_GETATTR(v1, &v1attr, ATTR_REAL, cr) != 0 || 795*7c478bd9Sstevel@tonic-gate VOP_GETATTR(v2, &v2attr, ATTR_REAL, cr) != 0) 796*7c478bd9Sstevel@tonic-gate return (0); 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate return (v1attr.va_fsid == v2attr.va_fsid && 799*7c478bd9Sstevel@tonic-gate v1attr.va_nodeid == v2attr.va_nodeid); 800*7c478bd9Sstevel@tonic-gate } 801*7c478bd9Sstevel@tonic-gate 802*7c478bd9Sstevel@tonic-gate 803*7c478bd9Sstevel@tonic-gate /* 804*7c478bd9Sstevel@tonic-gate * Find the entry in the directory corresponding to the target vnode. 805*7c478bd9Sstevel@tonic-gate */ 806*7c478bd9Sstevel@tonic-gate int 807*7c478bd9Sstevel@tonic-gate dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, 808*7c478bd9Sstevel@tonic-gate size_t dlen, dirent64_t **rdp) 809*7c478bd9Sstevel@tonic-gate { 810*7c478bd9Sstevel@tonic-gate size_t dbuflen; 811*7c478bd9Sstevel@tonic-gate struct iovec iov; 812*7c478bd9Sstevel@tonic-gate struct uio uio; 813*7c478bd9Sstevel@tonic-gate int err; 814*7c478bd9Sstevel@tonic-gate int eof; 815*7c478bd9Sstevel@tonic-gate vnode_t *cmpvp; 816*7c478bd9Sstevel@tonic-gate struct dirent64 *dp; 817*7c478bd9Sstevel@tonic-gate pathname_t pnp; 818*7c478bd9Sstevel@tonic-gate 819*7c478bd9Sstevel@tonic-gate ASSERT(dvp->v_type == VDIR); 820*7c478bd9Sstevel@tonic-gate 821*7c478bd9Sstevel@tonic-gate /* 822*7c478bd9Sstevel@tonic-gate * This is necessary because of the strange semantics of VOP_LOOKUP(). 823*7c478bd9Sstevel@tonic-gate */ 824*7c478bd9Sstevel@tonic-gate bzero(&pnp, sizeof (pnp)); 825*7c478bd9Sstevel@tonic-gate 826*7c478bd9Sstevel@tonic-gate eof = 0; 827*7c478bd9Sstevel@tonic-gate 828*7c478bd9Sstevel@tonic-gate uio.uio_iov = &iov; 829*7c478bd9Sstevel@tonic-gate uio.uio_iovcnt = 1; 830*7c478bd9Sstevel@tonic-gate uio.uio_segflg = UIO_SYSSPACE; 831*7c478bd9Sstevel@tonic-gate uio.uio_fmode = 0; 832*7c478bd9Sstevel@tonic-gate uio.uio_extflg = UIO_COPY_CACHED; 833*7c478bd9Sstevel@tonic-gate uio.uio_loffset = 0; 834*7c478bd9Sstevel@tonic-gate 835*7c478bd9Sstevel@tonic-gate if ((err = VOP_ACCESS(dvp, VREAD, 0, cr)) != 0) 836*7c478bd9Sstevel@tonic-gate return (err); 837*7c478bd9Sstevel@tonic-gate 838*7c478bd9Sstevel@tonic-gate while (!eof) { 839*7c478bd9Sstevel@tonic-gate uio.uio_resid = dlen; 840*7c478bd9Sstevel@tonic-gate iov.iov_base = dbuf; 841*7c478bd9Sstevel@tonic-gate iov.iov_len = dlen; 842*7c478bd9Sstevel@tonic-gate 843*7c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); 844*7c478bd9Sstevel@tonic-gate err = VOP_READDIR(dvp, &uio, cr, &eof); 845*7c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); 846*7c478bd9Sstevel@tonic-gate 847*7c478bd9Sstevel@tonic-gate dbuflen = dlen - uio.uio_resid; 848*7c478bd9Sstevel@tonic-gate 849*7c478bd9Sstevel@tonic-gate if (err || dbuflen == 0) 850*7c478bd9Sstevel@tonic-gate break; 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate dp = (dirent64_t *)dbuf; 853*7c478bd9Sstevel@tonic-gate while ((intptr_t)dp < (intptr_t)dbuf + dbuflen) { 854*7c478bd9Sstevel@tonic-gate /* 855*7c478bd9Sstevel@tonic-gate * Ignore '.' and '..' entries 856*7c478bd9Sstevel@tonic-gate */ 857*7c478bd9Sstevel@tonic-gate if (strcmp(dp->d_name, ".") == 0 || 858*7c478bd9Sstevel@tonic-gate strcmp(dp->d_name, "..") == 0) { 859*7c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + 860*7c478bd9Sstevel@tonic-gate dp->d_reclen); 861*7c478bd9Sstevel@tonic-gate continue; 862*7c478bd9Sstevel@tonic-gate } 863*7c478bd9Sstevel@tonic-gate 864*7c478bd9Sstevel@tonic-gate err = VOP_LOOKUP(dvp, dp->d_name, &cmpvp, &pnp, 0, 865*7c478bd9Sstevel@tonic-gate vrootp, cr); 866*7c478bd9Sstevel@tonic-gate 867*7c478bd9Sstevel@tonic-gate /* 868*7c478bd9Sstevel@tonic-gate * We only want to bail out if there was an error other 869*7c478bd9Sstevel@tonic-gate * than ENOENT. Otherwise, it could be that someone 870*7c478bd9Sstevel@tonic-gate * just removed an entry since the readdir() call, and 871*7c478bd9Sstevel@tonic-gate * the entry we want is further on in the directory. 872*7c478bd9Sstevel@tonic-gate */ 873*7c478bd9Sstevel@tonic-gate if (err == 0) { 874*7c478bd9Sstevel@tonic-gate if (vnode_match(tvp, cmpvp, cr)) { 875*7c478bd9Sstevel@tonic-gate VN_RELE(cmpvp); 876*7c478bd9Sstevel@tonic-gate *rdp = dp; 877*7c478bd9Sstevel@tonic-gate return (0); 878*7c478bd9Sstevel@tonic-gate } 879*7c478bd9Sstevel@tonic-gate 880*7c478bd9Sstevel@tonic-gate VN_RELE(cmpvp); 881*7c478bd9Sstevel@tonic-gate } else if (err != ENOENT) { 882*7c478bd9Sstevel@tonic-gate return (err); 883*7c478bd9Sstevel@tonic-gate } 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen); 886*7c478bd9Sstevel@tonic-gate } 887*7c478bd9Sstevel@tonic-gate } 888*7c478bd9Sstevel@tonic-gate 889*7c478bd9Sstevel@tonic-gate /* 890*7c478bd9Sstevel@tonic-gate * Something strange has happened, this directory does not contain the 891*7c478bd9Sstevel@tonic-gate * specified vnode. This should never happen in the normal case, since 892*7c478bd9Sstevel@tonic-gate * we ensured that dvp is the parent of vp. This may be possible in 893*7c478bd9Sstevel@tonic-gate * some race conditions, so fail gracefully. 894*7c478bd9Sstevel@tonic-gate */ 895*7c478bd9Sstevel@tonic-gate if (err == 0) 896*7c478bd9Sstevel@tonic-gate err = ENOENT; 897*7c478bd9Sstevel@tonic-gate 898*7c478bd9Sstevel@tonic-gate return (err); 899*7c478bd9Sstevel@tonic-gate } 900*7c478bd9Sstevel@tonic-gate 901*7c478bd9Sstevel@tonic-gate /* 902*7c478bd9Sstevel@tonic-gate * Given a global path (from rootdir), and a vnode that is the current root, 903*7c478bd9Sstevel@tonic-gate * return the portion of the path that is beneath the current root or NULL on 904*7c478bd9Sstevel@tonic-gate * failure. The path MUST be a resolved path (no '..' entries or symlinks), 905*7c478bd9Sstevel@tonic-gate * otherwise this function will fail. 906*7c478bd9Sstevel@tonic-gate */ 907*7c478bd9Sstevel@tonic-gate static char * 908*7c478bd9Sstevel@tonic-gate localpath(char *path, struct vnode *vrootp, cred_t *cr) 909*7c478bd9Sstevel@tonic-gate { 910*7c478bd9Sstevel@tonic-gate vnode_t *vp; 911*7c478bd9Sstevel@tonic-gate vnode_t *cvp; 912*7c478bd9Sstevel@tonic-gate char component[MAXNAMELEN]; 913*7c478bd9Sstevel@tonic-gate char *ret = NULL; 914*7c478bd9Sstevel@tonic-gate pathname_t pn; 915*7c478bd9Sstevel@tonic-gate 916*7c478bd9Sstevel@tonic-gate /* 917*7c478bd9Sstevel@tonic-gate * We use vn_compare() instead of VN_CMP() in order to detect lofs 918*7c478bd9Sstevel@tonic-gate * mounts and stacked vnodes. 919*7c478bd9Sstevel@tonic-gate */ 920*7c478bd9Sstevel@tonic-gate if (vn_compare(vrootp, rootdir)) 921*7c478bd9Sstevel@tonic-gate return (path); 922*7c478bd9Sstevel@tonic-gate 923*7c478bd9Sstevel@tonic-gate if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 924*7c478bd9Sstevel@tonic-gate return (NULL); 925*7c478bd9Sstevel@tonic-gate 926*7c478bd9Sstevel@tonic-gate vp = rootdir; 927*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 928*7c478bd9Sstevel@tonic-gate 929*7c478bd9Sstevel@tonic-gate while (pn_pathleft(&pn)) { 930*7c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 931*7c478bd9Sstevel@tonic-gate 932*7c478bd9Sstevel@tonic-gate if (pn_getcomponent(&pn, component) != 0) 933*7c478bd9Sstevel@tonic-gate break; 934*7c478bd9Sstevel@tonic-gate 935*7c478bd9Sstevel@tonic-gate if (vn_ismntpt(vp) && traverse(&vp) != 0) 936*7c478bd9Sstevel@tonic-gate break; 937*7c478bd9Sstevel@tonic-gate 938*7c478bd9Sstevel@tonic-gate if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr) != 0) 939*7c478bd9Sstevel@tonic-gate break; 940*7c478bd9Sstevel@tonic-gate 941*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 942*7c478bd9Sstevel@tonic-gate vp = cvp; 943*7c478bd9Sstevel@tonic-gate 944*7c478bd9Sstevel@tonic-gate if (vn_compare(vp, vrootp)) { 945*7c478bd9Sstevel@tonic-gate ret = path + (pn.pn_path - pn.pn_buf); 946*7c478bd9Sstevel@tonic-gate break; 947*7c478bd9Sstevel@tonic-gate } 948*7c478bd9Sstevel@tonic-gate } 949*7c478bd9Sstevel@tonic-gate 950*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 951*7c478bd9Sstevel@tonic-gate pn_free(&pn); 952*7c478bd9Sstevel@tonic-gate 953*7c478bd9Sstevel@tonic-gate return (ret); 954*7c478bd9Sstevel@tonic-gate } 955*7c478bd9Sstevel@tonic-gate 956*7c478bd9Sstevel@tonic-gate /* 957*7c478bd9Sstevel@tonic-gate * Given a directory, return the full, resolved path. This looks up "..", 958*7c478bd9Sstevel@tonic-gate * searches for the given vnode in the parent, appends the component, etc. It 959*7c478bd9Sstevel@tonic-gate * is used to implement vnodetopath() and getcwd() when the cached path fails 960*7c478bd9Sstevel@tonic-gate * (or vfs_vnode_path is not set). 961*7c478bd9Sstevel@tonic-gate */ 962*7c478bd9Sstevel@tonic-gate static int 963*7c478bd9Sstevel@tonic-gate dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr) 964*7c478bd9Sstevel@tonic-gate { 965*7c478bd9Sstevel@tonic-gate pathname_t pn, rpn, emptypn; 966*7c478bd9Sstevel@tonic-gate vnode_t *cmpvp, *pvp = NULL; 967*7c478bd9Sstevel@tonic-gate vnode_t *startvp = vp; 968*7c478bd9Sstevel@tonic-gate int err = 0; 969*7c478bd9Sstevel@tonic-gate size_t complen; 970*7c478bd9Sstevel@tonic-gate char *dbuf; 971*7c478bd9Sstevel@tonic-gate dirent64_t *dp; 972*7c478bd9Sstevel@tonic-gate char *bufloc; 973*7c478bd9Sstevel@tonic-gate size_t dlen = DIRENT64_RECLEN(MAXPATHLEN); 974*7c478bd9Sstevel@tonic-gate refstr_t *mntpt; 975*7c478bd9Sstevel@tonic-gate 976*7c478bd9Sstevel@tonic-gate /* Operation only allowed on directories */ 977*7c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR); 978*7c478bd9Sstevel@tonic-gate 979*7c478bd9Sstevel@tonic-gate /* We must have at least enough space for "/" */ 980*7c478bd9Sstevel@tonic-gate if (buflen < 2) 981*7c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate /* Start at end of string with terminating null */ 984*7c478bd9Sstevel@tonic-gate bufloc = &buf[buflen - 1]; 985*7c478bd9Sstevel@tonic-gate *bufloc = '\0'; 986*7c478bd9Sstevel@tonic-gate 987*7c478bd9Sstevel@tonic-gate pn_alloc(&pn); 988*7c478bd9Sstevel@tonic-gate pn_alloc(&rpn); 989*7c478bd9Sstevel@tonic-gate dbuf = kmem_alloc(dlen, KM_SLEEP); 990*7c478bd9Sstevel@tonic-gate bzero(&emptypn, sizeof (emptypn)); 991*7c478bd9Sstevel@tonic-gate 992*7c478bd9Sstevel@tonic-gate /* 993*7c478bd9Sstevel@tonic-gate * Begin with an additional reference on vp. This will be decremented 994*7c478bd9Sstevel@tonic-gate * during the loop. 995*7c478bd9Sstevel@tonic-gate */ 996*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 997*7c478bd9Sstevel@tonic-gate 998*7c478bd9Sstevel@tonic-gate for (;;) { 999*7c478bd9Sstevel@tonic-gate /* 1000*7c478bd9Sstevel@tonic-gate * Return if we've reached the root. If the buffer is empty, 1001*7c478bd9Sstevel@tonic-gate * return '/'. We explicitly don't use vn_compare(), since it 1002*7c478bd9Sstevel@tonic-gate * compares the real vnodes. A lofs mount of '/' would produce 1003*7c478bd9Sstevel@tonic-gate * incorrect results otherwise. 1004*7c478bd9Sstevel@tonic-gate */ 1005*7c478bd9Sstevel@tonic-gate if (VN_CMP(vrootp, vp)) { 1006*7c478bd9Sstevel@tonic-gate if (*bufloc == '\0') 1007*7c478bd9Sstevel@tonic-gate *--bufloc = '/'; 1008*7c478bd9Sstevel@tonic-gate break; 1009*7c478bd9Sstevel@tonic-gate } 1010*7c478bd9Sstevel@tonic-gate 1011*7c478bd9Sstevel@tonic-gate /* 1012*7c478bd9Sstevel@tonic-gate * If we've reached the VFS root, something has gone wrong. We 1013*7c478bd9Sstevel@tonic-gate * should have reached the root in the above check. The only 1014*7c478bd9Sstevel@tonic-gate * explantation is that 'vp' is not contained withing the given 1015*7c478bd9Sstevel@tonic-gate * root, in which case we return EPERM. 1016*7c478bd9Sstevel@tonic-gate */ 1017*7c478bd9Sstevel@tonic-gate if (VN_CMP(rootdir, vp)) { 1018*7c478bd9Sstevel@tonic-gate err = EPERM; 1019*7c478bd9Sstevel@tonic-gate goto out; 1020*7c478bd9Sstevel@tonic-gate } 1021*7c478bd9Sstevel@tonic-gate 1022*7c478bd9Sstevel@tonic-gate /* 1023*7c478bd9Sstevel@tonic-gate * Shortcut: see if this vnode is a mountpoint. If so, 1024*7c478bd9Sstevel@tonic-gate * grab the path information from the vfs_t. 1025*7c478bd9Sstevel@tonic-gate */ 1026*7c478bd9Sstevel@tonic-gate if (vp->v_flag & VROOT) { 1027*7c478bd9Sstevel@tonic-gate 1028*7c478bd9Sstevel@tonic-gate mntpt = vfs_getmntpoint(vp->v_vfsp); 1029*7c478bd9Sstevel@tonic-gate if ((err = pn_set(&pn, (char *)refstr_value(mntpt))) 1030*7c478bd9Sstevel@tonic-gate == 0) { 1031*7c478bd9Sstevel@tonic-gate refstr_rele(mntpt); 1032*7c478bd9Sstevel@tonic-gate rpn.pn_path = rpn.pn_buf; 1033*7c478bd9Sstevel@tonic-gate 1034*7c478bd9Sstevel@tonic-gate /* 1035*7c478bd9Sstevel@tonic-gate * Ensure the mointpoint still exists. 1036*7c478bd9Sstevel@tonic-gate */ 1037*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1038*7c478bd9Sstevel@tonic-gate if (vrootp != rootdir) 1039*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1040*7c478bd9Sstevel@tonic-gate if (lookuppnvp(&pn, &rpn, 0, NULL, 1041*7c478bd9Sstevel@tonic-gate &cmpvp, vrootp, vrootp, cr) == 0) { 1042*7c478bd9Sstevel@tonic-gate 1043*7c478bd9Sstevel@tonic-gate if (VN_CMP(vp, cmpvp)) { 1044*7c478bd9Sstevel@tonic-gate VN_RELE(cmpvp); 1045*7c478bd9Sstevel@tonic-gate 1046*7c478bd9Sstevel@tonic-gate complen = strlen(rpn.pn_path); 1047*7c478bd9Sstevel@tonic-gate bufloc -= complen; 1048*7c478bd9Sstevel@tonic-gate if (bufloc < buf) { 1049*7c478bd9Sstevel@tonic-gate err = ERANGE; 1050*7c478bd9Sstevel@tonic-gate goto out; 1051*7c478bd9Sstevel@tonic-gate } 1052*7c478bd9Sstevel@tonic-gate bcopy(rpn.pn_path, bufloc, 1053*7c478bd9Sstevel@tonic-gate complen); 1054*7c478bd9Sstevel@tonic-gate break; 1055*7c478bd9Sstevel@tonic-gate } else { 1056*7c478bd9Sstevel@tonic-gate VN_RELE(cmpvp); 1057*7c478bd9Sstevel@tonic-gate } 1058*7c478bd9Sstevel@tonic-gate } 1059*7c478bd9Sstevel@tonic-gate } else { 1060*7c478bd9Sstevel@tonic-gate refstr_rele(mntpt); 1061*7c478bd9Sstevel@tonic-gate } 1062*7c478bd9Sstevel@tonic-gate } 1063*7c478bd9Sstevel@tonic-gate 1064*7c478bd9Sstevel@tonic-gate /* 1065*7c478bd9Sstevel@tonic-gate * Shortcuts failed, search for this vnode in its parent. If 1066*7c478bd9Sstevel@tonic-gate * this is a mountpoint, then get the vnode underneath. 1067*7c478bd9Sstevel@tonic-gate */ 1068*7c478bd9Sstevel@tonic-gate if (vp->v_flag & VROOT) 1069*7c478bd9Sstevel@tonic-gate vp = vn_under(vp); 1070*7c478bd9Sstevel@tonic-gate if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr)) 1071*7c478bd9Sstevel@tonic-gate != 0) 1072*7c478bd9Sstevel@tonic-gate goto out; 1073*7c478bd9Sstevel@tonic-gate 1074*7c478bd9Sstevel@tonic-gate /* 1075*7c478bd9Sstevel@tonic-gate * With extended attributes, it's possible for a directory to 1076*7c478bd9Sstevel@tonic-gate * have a parent that is a regular file. Check for that here. 1077*7c478bd9Sstevel@tonic-gate */ 1078*7c478bd9Sstevel@tonic-gate if (pvp->v_type != VDIR) { 1079*7c478bd9Sstevel@tonic-gate err = ENOTDIR; 1080*7c478bd9Sstevel@tonic-gate goto out; 1081*7c478bd9Sstevel@tonic-gate } 1082*7c478bd9Sstevel@tonic-gate 1083*7c478bd9Sstevel@tonic-gate /* 1084*7c478bd9Sstevel@tonic-gate * If this is true, something strange has happened. This is 1085*7c478bd9Sstevel@tonic-gate * only true if we are the root of a filesystem, which should 1086*7c478bd9Sstevel@tonic-gate * have been caught by the check above. 1087*7c478bd9Sstevel@tonic-gate */ 1088*7c478bd9Sstevel@tonic-gate if (VN_CMP(pvp, vp)) { 1089*7c478bd9Sstevel@tonic-gate err = ENOENT; 1090*7c478bd9Sstevel@tonic-gate goto out; 1091*7c478bd9Sstevel@tonic-gate } 1092*7c478bd9Sstevel@tonic-gate 1093*7c478bd9Sstevel@tonic-gate /* 1094*7c478bd9Sstevel@tonic-gate * Search the parent directory for the entry corresponding to 1095*7c478bd9Sstevel@tonic-gate * this vnode. 1096*7c478bd9Sstevel@tonic-gate */ 1097*7c478bd9Sstevel@tonic-gate if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp)) 1098*7c478bd9Sstevel@tonic-gate != 0) 1099*7c478bd9Sstevel@tonic-gate goto out; 1100*7c478bd9Sstevel@tonic-gate complen = strlen(dp->d_name); 1101*7c478bd9Sstevel@tonic-gate bufloc -= complen; 1102*7c478bd9Sstevel@tonic-gate if (bufloc <= buf) { 1103*7c478bd9Sstevel@tonic-gate err = ENAMETOOLONG; 1104*7c478bd9Sstevel@tonic-gate goto out; 1105*7c478bd9Sstevel@tonic-gate } 1106*7c478bd9Sstevel@tonic-gate bcopy(dp->d_name, bufloc, complen); 1107*7c478bd9Sstevel@tonic-gate 1108*7c478bd9Sstevel@tonic-gate /* Prepend a slash to the current path. */ 1109*7c478bd9Sstevel@tonic-gate *--bufloc = '/'; 1110*7c478bd9Sstevel@tonic-gate 1111*7c478bd9Sstevel@tonic-gate /* And continue with the next component */ 1112*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1113*7c478bd9Sstevel@tonic-gate vp = pvp; 1114*7c478bd9Sstevel@tonic-gate pvp = NULL; 1115*7c478bd9Sstevel@tonic-gate } 1116*7c478bd9Sstevel@tonic-gate 1117*7c478bd9Sstevel@tonic-gate /* 1118*7c478bd9Sstevel@tonic-gate * Place the path at the beginning of the buffer. 1119*7c478bd9Sstevel@tonic-gate */ 1120*7c478bd9Sstevel@tonic-gate if (bufloc != buf) 1121*7c478bd9Sstevel@tonic-gate ovbcopy(bufloc, buf, buflen - (bufloc - buf)); 1122*7c478bd9Sstevel@tonic-gate 1123*7c478bd9Sstevel@tonic-gate out: 1124*7c478bd9Sstevel@tonic-gate /* 1125*7c478bd9Sstevel@tonic-gate * If the error was ESTALE and the current directory to look in 1126*7c478bd9Sstevel@tonic-gate * was the root for this lookup, the root for a mounted file 1127*7c478bd9Sstevel@tonic-gate * system, or the starting directory for lookups, then 1128*7c478bd9Sstevel@tonic-gate * return ENOENT instead of ESTALE. In this case, no recovery 1129*7c478bd9Sstevel@tonic-gate * is possible by the higher level. If ESTALE was returned for 1130*7c478bd9Sstevel@tonic-gate * some intermediate directory along the path, then recovery 1131*7c478bd9Sstevel@tonic-gate * is potentially possible and retrying from the higher level 1132*7c478bd9Sstevel@tonic-gate * will either correct the situation by purging stale cache 1133*7c478bd9Sstevel@tonic-gate * entries or eventually get back to the point where no recovery 1134*7c478bd9Sstevel@tonic-gate * is possible. 1135*7c478bd9Sstevel@tonic-gate */ 1136*7c478bd9Sstevel@tonic-gate if (err == ESTALE && 1137*7c478bd9Sstevel@tonic-gate (VN_CMP(vp, vrootp) || (vp->v_flag & VROOT) || vp == startvp)) 1138*7c478bd9Sstevel@tonic-gate err = ENOENT; 1139*7c478bd9Sstevel@tonic-gate 1140*7c478bd9Sstevel@tonic-gate kmem_free(dbuf, dlen); 1141*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1142*7c478bd9Sstevel@tonic-gate if (pvp) 1143*7c478bd9Sstevel@tonic-gate VN_RELE(pvp); 1144*7c478bd9Sstevel@tonic-gate pn_free(&pn); 1145*7c478bd9Sstevel@tonic-gate pn_free(&rpn); 1146*7c478bd9Sstevel@tonic-gate 1147*7c478bd9Sstevel@tonic-gate return (err); 1148*7c478bd9Sstevel@tonic-gate } 1149*7c478bd9Sstevel@tonic-gate 1150*7c478bd9Sstevel@tonic-gate /* 1151*7c478bd9Sstevel@tonic-gate * The additional flag, LOOKUP_CHECKREAD, is ued to enforce artificial 1152*7c478bd9Sstevel@tonic-gate * constraints in order to be standards compliant. For example, if we have 1153*7c478bd9Sstevel@tonic-gate * the cached path of '/foo/bar', and '/foo' has permissions 100 (execute 1154*7c478bd9Sstevel@tonic-gate * only), then we can legitimately look up the path to the current working 1155*7c478bd9Sstevel@tonic-gate * directory without needing read permission. Existing standards tests, 1156*7c478bd9Sstevel@tonic-gate * however, assume that we are determining the path by repeatedly looking up 1157*7c478bd9Sstevel@tonic-gate * "..". We need to keep this behavior in order to maintain backwards 1158*7c478bd9Sstevel@tonic-gate * compatibility. 1159*7c478bd9Sstevel@tonic-gate */ 1160*7c478bd9Sstevel@tonic-gate static int 1161*7c478bd9Sstevel@tonic-gate vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, 1162*7c478bd9Sstevel@tonic-gate cred_t *cr, int flags) 1163*7c478bd9Sstevel@tonic-gate { 1164*7c478bd9Sstevel@tonic-gate pathname_t pn, rpn; 1165*7c478bd9Sstevel@tonic-gate int ret, len; 1166*7c478bd9Sstevel@tonic-gate vnode_t *compvp, *pvp, *realvp; 1167*7c478bd9Sstevel@tonic-gate proc_t *p = curproc; 1168*7c478bd9Sstevel@tonic-gate char path[MAXNAMELEN]; 1169*7c478bd9Sstevel@tonic-gate int doclose = 0; 1170*7c478bd9Sstevel@tonic-gate 1171*7c478bd9Sstevel@tonic-gate /* 1172*7c478bd9Sstevel@tonic-gate * If vrootp is NULL, get the root for curproc. Callers with any other 1173*7c478bd9Sstevel@tonic-gate * requirements should pass in a different vrootp. 1174*7c478bd9Sstevel@tonic-gate */ 1175*7c478bd9Sstevel@tonic-gate if (vrootp == NULL) { 1176*7c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 1177*7c478bd9Sstevel@tonic-gate if ((vrootp = PTOU(p)->u_rdir) == NULL) 1178*7c478bd9Sstevel@tonic-gate vrootp = rootdir; 1179*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1180*7c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 1181*7c478bd9Sstevel@tonic-gate } else { 1182*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1183*7c478bd9Sstevel@tonic-gate } 1184*7c478bd9Sstevel@tonic-gate 1185*7c478bd9Sstevel@tonic-gate /* 1186*7c478bd9Sstevel@tonic-gate * This is to get around an annoying artifact of the /proc filesystem, 1187*7c478bd9Sstevel@tonic-gate * which is the behavior of {cwd/root}. Trying to resolve this path 1188*7c478bd9Sstevel@tonic-gate * will result in /proc/pid/cwd instead of whatever the real working 1189*7c478bd9Sstevel@tonic-gate * directory is. We can't rely on VOP_REALVP(), since that will break 1190*7c478bd9Sstevel@tonic-gate * lofs. The only difference between procfs and lofs is that opening 1191*7c478bd9Sstevel@tonic-gate * the file will return the underling vnode in the case of procfs. 1192*7c478bd9Sstevel@tonic-gate */ 1193*7c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp) == 0 && 1194*7c478bd9Sstevel@tonic-gate realvp != vp) { 1195*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 1196*7c478bd9Sstevel@tonic-gate if (VOP_OPEN(&vp, FREAD, cr) == 0) 1197*7c478bd9Sstevel@tonic-gate doclose = 1; 1198*7c478bd9Sstevel@tonic-gate else 1199*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1200*7c478bd9Sstevel@tonic-gate } 1201*7c478bd9Sstevel@tonic-gate 1202*7c478bd9Sstevel@tonic-gate pn_alloc(&pn); 1203*7c478bd9Sstevel@tonic-gate 1204*7c478bd9Sstevel@tonic-gate /* 1205*7c478bd9Sstevel@tonic-gate * Check to see if we have a cached path in the vnode. 1206*7c478bd9Sstevel@tonic-gate */ 1207*7c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1208*7c478bd9Sstevel@tonic-gate if (vn_path(vp) != NULL) { 1209*7c478bd9Sstevel@tonic-gate (void) pn_set(&pn, vn_path(vp)); 1210*7c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1211*7c478bd9Sstevel@tonic-gate 1212*7c478bd9Sstevel@tonic-gate pn_alloc(&rpn); 1213*7c478bd9Sstevel@tonic-gate 1214*7c478bd9Sstevel@tonic-gate /* We should only cache absolute paths */ 1215*7c478bd9Sstevel@tonic-gate ASSERT(pn.pn_buf[0] == '/'); 1216*7c478bd9Sstevel@tonic-gate 1217*7c478bd9Sstevel@tonic-gate /* 1218*7c478bd9Sstevel@tonic-gate * If we are in a zone or a chroot environment, then we have to 1219*7c478bd9Sstevel@tonic-gate * take additional steps, since the path to the root might not 1220*7c478bd9Sstevel@tonic-gate * be readable with the current credentials, even though the 1221*7c478bd9Sstevel@tonic-gate * process can legitmately access the file. In this case, we 1222*7c478bd9Sstevel@tonic-gate * do the following: 1223*7c478bd9Sstevel@tonic-gate * 1224*7c478bd9Sstevel@tonic-gate * lookuppnvp() with all privileges to get the resolved path. 1225*7c478bd9Sstevel@tonic-gate * call localpath() to get the local portion of the path, and 1226*7c478bd9Sstevel@tonic-gate * continue as normal. 1227*7c478bd9Sstevel@tonic-gate * 1228*7c478bd9Sstevel@tonic-gate * If the the conversion to a local path fails, then we continue 1229*7c478bd9Sstevel@tonic-gate * as normal. This is a heuristic to make process object file 1230*7c478bd9Sstevel@tonic-gate * paths available from within a zone. Because lofs doesn't 1231*7c478bd9Sstevel@tonic-gate * support page operations, the vnode stored in the seg_t is 1232*7c478bd9Sstevel@tonic-gate * actually the underlying real vnode, not the lofs node itself. 1233*7c478bd9Sstevel@tonic-gate * Most of the time, the lofs path is the same as the underlying 1234*7c478bd9Sstevel@tonic-gate * vnode (for example, /usr/lib/libc.so.1). 1235*7c478bd9Sstevel@tonic-gate */ 1236*7c478bd9Sstevel@tonic-gate if (vrootp != rootdir) { 1237*7c478bd9Sstevel@tonic-gate char *local = NULL; 1238*7c478bd9Sstevel@tonic-gate VN_HOLD(rootdir); 1239*7c478bd9Sstevel@tonic-gate if (lookuppnvp(&pn, &rpn, FOLLOW, 1240*7c478bd9Sstevel@tonic-gate NULL, &compvp, rootdir, rootdir, kcred) == 0) { 1241*7c478bd9Sstevel@tonic-gate local = localpath(rpn.pn_path, vrootp, 1242*7c478bd9Sstevel@tonic-gate kcred); 1243*7c478bd9Sstevel@tonic-gate VN_RELE(compvp); 1244*7c478bd9Sstevel@tonic-gate } 1245*7c478bd9Sstevel@tonic-gate 1246*7c478bd9Sstevel@tonic-gate /* 1247*7c478bd9Sstevel@tonic-gate * The original pn was changed through lookuppnvp(), so 1248*7c478bd9Sstevel@tonic-gate * reset it. 1249*7c478bd9Sstevel@tonic-gate */ 1250*7c478bd9Sstevel@tonic-gate if (local) { 1251*7c478bd9Sstevel@tonic-gate (void) pn_set(&pn, local); 1252*7c478bd9Sstevel@tonic-gate } else { 1253*7c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1254*7c478bd9Sstevel@tonic-gate if (vn_path(vp) != NULL) { 1255*7c478bd9Sstevel@tonic-gate (void) pn_set(&pn, vn_path(vp)); 1256*7c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1257*7c478bd9Sstevel@tonic-gate } else { 1258*7c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1259*7c478bd9Sstevel@tonic-gate goto notcached; 1260*7c478bd9Sstevel@tonic-gate } 1261*7c478bd9Sstevel@tonic-gate } 1262*7c478bd9Sstevel@tonic-gate } 1263*7c478bd9Sstevel@tonic-gate 1264*7c478bd9Sstevel@tonic-gate /* 1265*7c478bd9Sstevel@tonic-gate * We should have a local path at this point, so start the 1266*7c478bd9Sstevel@tonic-gate * search from the root of the current process. 1267*7c478bd9Sstevel@tonic-gate */ 1268*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1269*7c478bd9Sstevel@tonic-gate if (vrootp != rootdir) 1270*7c478bd9Sstevel@tonic-gate VN_HOLD(vrootp); 1271*7c478bd9Sstevel@tonic-gate ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL, 1272*7c478bd9Sstevel@tonic-gate &compvp, vrootp, vrootp, cr); 1273*7c478bd9Sstevel@tonic-gate if (ret == 0) { 1274*7c478bd9Sstevel@tonic-gate /* 1275*7c478bd9Sstevel@tonic-gate * Check to see if the returned vnode is the same as 1276*7c478bd9Sstevel@tonic-gate * the one we expect. If not, give up. 1277*7c478bd9Sstevel@tonic-gate */ 1278*7c478bd9Sstevel@tonic-gate if (!vn_compare(vp, compvp) && 1279*7c478bd9Sstevel@tonic-gate !vnode_match(vp, compvp, cr)) { 1280*7c478bd9Sstevel@tonic-gate VN_RELE(compvp); 1281*7c478bd9Sstevel@tonic-gate goto notcached; 1282*7c478bd9Sstevel@tonic-gate } 1283*7c478bd9Sstevel@tonic-gate 1284*7c478bd9Sstevel@tonic-gate VN_RELE(compvp); 1285*7c478bd9Sstevel@tonic-gate 1286*7c478bd9Sstevel@tonic-gate /* 1287*7c478bd9Sstevel@tonic-gate * Return the result. 1288*7c478bd9Sstevel@tonic-gate */ 1289*7c478bd9Sstevel@tonic-gate if (buflen <= rpn.pn_pathlen) 1290*7c478bd9Sstevel@tonic-gate goto notcached; 1291*7c478bd9Sstevel@tonic-gate 1292*7c478bd9Sstevel@tonic-gate bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1); 1293*7c478bd9Sstevel@tonic-gate pn_free(&pn); 1294*7c478bd9Sstevel@tonic-gate pn_free(&rpn); 1295*7c478bd9Sstevel@tonic-gate VN_RELE(vrootp); 1296*7c478bd9Sstevel@tonic-gate if (doclose) { 1297*7c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(vp, FREAD, 1, 0, cr); 1298*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1299*7c478bd9Sstevel@tonic-gate } 1300*7c478bd9Sstevel@tonic-gate return (0); 1301*7c478bd9Sstevel@tonic-gate } 1302*7c478bd9Sstevel@tonic-gate 1303*7c478bd9Sstevel@tonic-gate notcached: 1304*7c478bd9Sstevel@tonic-gate pn_free(&rpn); 1305*7c478bd9Sstevel@tonic-gate } else { 1306*7c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1307*7c478bd9Sstevel@tonic-gate } 1308*7c478bd9Sstevel@tonic-gate 1309*7c478bd9Sstevel@tonic-gate pn_free(&pn); 1310*7c478bd9Sstevel@tonic-gate 1311*7c478bd9Sstevel@tonic-gate if (vp->v_type != VDIR) { 1312*7c478bd9Sstevel@tonic-gate /* 1313*7c478bd9Sstevel@tonic-gate * If we don't have a directory, try to find it in the dnlc via 1314*7c478bd9Sstevel@tonic-gate * reverse lookup. Once this is found, we can use the regular 1315*7c478bd9Sstevel@tonic-gate * directory search to find the full path. 1316*7c478bd9Sstevel@tonic-gate */ 1317*7c478bd9Sstevel@tonic-gate if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) { 1318*7c478bd9Sstevel@tonic-gate ret = dirtopath(vrootp, pvp, buf, buflen, cr); 1319*7c478bd9Sstevel@tonic-gate if (ret == 0) { 1320*7c478bd9Sstevel@tonic-gate len = strlen(buf); 1321*7c478bd9Sstevel@tonic-gate if (len + strlen(path) + 1 >= buflen) { 1322*7c478bd9Sstevel@tonic-gate ret = ENAMETOOLONG; 1323*7c478bd9Sstevel@tonic-gate } else { 1324*7c478bd9Sstevel@tonic-gate if (buf[len - 1] != '/') 1325*7c478bd9Sstevel@tonic-gate buf[len++] = '/'; 1326*7c478bd9Sstevel@tonic-gate bcopy(path, buf + len, 1327*7c478bd9Sstevel@tonic-gate strlen(path) + 1); 1328*7c478bd9Sstevel@tonic-gate } 1329*7c478bd9Sstevel@tonic-gate } 1330*7c478bd9Sstevel@tonic-gate 1331*7c478bd9Sstevel@tonic-gate VN_RELE(pvp); 1332*7c478bd9Sstevel@tonic-gate } else 1333*7c478bd9Sstevel@tonic-gate ret = ENOENT; 1334*7c478bd9Sstevel@tonic-gate } else 1335*7c478bd9Sstevel@tonic-gate ret = dirtopath(vrootp, vp, buf, buflen, cr); 1336*7c478bd9Sstevel@tonic-gate 1337*7c478bd9Sstevel@tonic-gate VN_RELE(vrootp); 1338*7c478bd9Sstevel@tonic-gate if (doclose) { 1339*7c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(vp, FREAD, 1, 0, cr); 1340*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1341*7c478bd9Sstevel@tonic-gate } 1342*7c478bd9Sstevel@tonic-gate 1343*7c478bd9Sstevel@tonic-gate return (ret); 1344*7c478bd9Sstevel@tonic-gate } 1345*7c478bd9Sstevel@tonic-gate 1346*7c478bd9Sstevel@tonic-gate int 1347*7c478bd9Sstevel@tonic-gate vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr) 1348*7c478bd9Sstevel@tonic-gate { 1349*7c478bd9Sstevel@tonic-gate return (vnodetopath_common(vrootp, vp, buf, buflen, cr, 0)); 1350*7c478bd9Sstevel@tonic-gate } 1351*7c478bd9Sstevel@tonic-gate 1352*7c478bd9Sstevel@tonic-gate int 1353*7c478bd9Sstevel@tonic-gate dogetcwd(char *buf, size_t buflen) 1354*7c478bd9Sstevel@tonic-gate { 1355*7c478bd9Sstevel@tonic-gate int ret; 1356*7c478bd9Sstevel@tonic-gate vnode_t *vp; 1357*7c478bd9Sstevel@tonic-gate vnode_t *compvp; 1358*7c478bd9Sstevel@tonic-gate refstr_t *cwd, *oldcwd; 1359*7c478bd9Sstevel@tonic-gate const char *value; 1360*7c478bd9Sstevel@tonic-gate pathname_t rpnp, pnp; 1361*7c478bd9Sstevel@tonic-gate proc_t *p = curproc; 1362*7c478bd9Sstevel@tonic-gate 1363*7c478bd9Sstevel@tonic-gate /* 1364*7c478bd9Sstevel@tonic-gate * Check to see if there is a cached version of the cwd. If so, lookup 1365*7c478bd9Sstevel@tonic-gate * the cached value and make sure it is the same vnode. 1366*7c478bd9Sstevel@tonic-gate */ 1367*7c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 1368*7c478bd9Sstevel@tonic-gate if ((cwd = PTOU(p)->u_cwd) != NULL) 1369*7c478bd9Sstevel@tonic-gate refstr_hold(cwd); 1370*7c478bd9Sstevel@tonic-gate vp = PTOU(p)->u_cdir; 1371*7c478bd9Sstevel@tonic-gate VN_HOLD(vp); 1372*7c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 1373*7c478bd9Sstevel@tonic-gate 1374*7c478bd9Sstevel@tonic-gate /* 1375*7c478bd9Sstevel@tonic-gate * Make sure we have permission to access the current directory. 1376*7c478bd9Sstevel@tonic-gate */ 1377*7c478bd9Sstevel@tonic-gate if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED())) != 0) { 1378*7c478bd9Sstevel@tonic-gate if (cwd != NULL) 1379*7c478bd9Sstevel@tonic-gate refstr_rele(cwd); 1380*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1381*7c478bd9Sstevel@tonic-gate return (ret); 1382*7c478bd9Sstevel@tonic-gate } 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate if (cwd) { 1385*7c478bd9Sstevel@tonic-gate value = refstr_value(cwd); 1386*7c478bd9Sstevel@tonic-gate if ((ret = pn_get((char *)value, UIO_SYSSPACE, &pnp)) != 0) { 1387*7c478bd9Sstevel@tonic-gate refstr_rele(cwd); 1388*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1389*7c478bd9Sstevel@tonic-gate return (ret); 1390*7c478bd9Sstevel@tonic-gate } 1391*7c478bd9Sstevel@tonic-gate 1392*7c478bd9Sstevel@tonic-gate pn_alloc(&rpnp); 1393*7c478bd9Sstevel@tonic-gate 1394*7c478bd9Sstevel@tonic-gate if (lookuppn(&pnp, &rpnp, NO_FOLLOW, NULL, &compvp) == 0) { 1395*7c478bd9Sstevel@tonic-gate 1396*7c478bd9Sstevel@tonic-gate if (VN_CMP(vp, compvp) && 1397*7c478bd9Sstevel@tonic-gate strcmp(value, rpnp.pn_path) == 0) { 1398*7c478bd9Sstevel@tonic-gate VN_RELE(compvp); 1399*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1400*7c478bd9Sstevel@tonic-gate pn_free(&pnp); 1401*7c478bd9Sstevel@tonic-gate pn_free(&rpnp); 1402*7c478bd9Sstevel@tonic-gate if (strlen(value) + 1 > buflen) { 1403*7c478bd9Sstevel@tonic-gate refstr_rele(cwd); 1404*7c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 1405*7c478bd9Sstevel@tonic-gate } 1406*7c478bd9Sstevel@tonic-gate bcopy(value, buf, strlen(value) + 1); 1407*7c478bd9Sstevel@tonic-gate refstr_rele(cwd); 1408*7c478bd9Sstevel@tonic-gate return (0); 1409*7c478bd9Sstevel@tonic-gate } 1410*7c478bd9Sstevel@tonic-gate 1411*7c478bd9Sstevel@tonic-gate VN_RELE(compvp); 1412*7c478bd9Sstevel@tonic-gate } 1413*7c478bd9Sstevel@tonic-gate 1414*7c478bd9Sstevel@tonic-gate pn_free(&rpnp); 1415*7c478bd9Sstevel@tonic-gate pn_free(&pnp); 1416*7c478bd9Sstevel@tonic-gate 1417*7c478bd9Sstevel@tonic-gate refstr_rele(cwd); 1418*7c478bd9Sstevel@tonic-gate } 1419*7c478bd9Sstevel@tonic-gate 1420*7c478bd9Sstevel@tonic-gate ret = vnodetopath_common(NULL, vp, buf, buflen, CRED(), 1421*7c478bd9Sstevel@tonic-gate LOOKUP_CHECKREAD); 1422*7c478bd9Sstevel@tonic-gate 1423*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 1424*7c478bd9Sstevel@tonic-gate 1425*7c478bd9Sstevel@tonic-gate /* 1426*7c478bd9Sstevel@tonic-gate * Store the new cwd and replace the existing cached copy. 1427*7c478bd9Sstevel@tonic-gate */ 1428*7c478bd9Sstevel@tonic-gate if (ret == 0) 1429*7c478bd9Sstevel@tonic-gate cwd = refstr_alloc(buf); 1430*7c478bd9Sstevel@tonic-gate else 1431*7c478bd9Sstevel@tonic-gate cwd = NULL; 1432*7c478bd9Sstevel@tonic-gate 1433*7c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 1434*7c478bd9Sstevel@tonic-gate oldcwd = PTOU(p)->u_cwd; 1435*7c478bd9Sstevel@tonic-gate PTOU(p)->u_cwd = cwd; 1436*7c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 1437*7c478bd9Sstevel@tonic-gate 1438*7c478bd9Sstevel@tonic-gate if (oldcwd) 1439*7c478bd9Sstevel@tonic-gate refstr_rele(oldcwd); 1440*7c478bd9Sstevel@tonic-gate 1441*7c478bd9Sstevel@tonic-gate return (ret); 1442*7c478bd9Sstevel@tonic-gate } 1443