Print this page

        

*** 19,29 **** * CDDL HEADER END */ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. ! * Copyright 2016 Joyent, Inc. * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ --- 19,29 ---- * CDDL HEADER END */ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. ! * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */
*** 977,1105 **** return (ret); } /* - * Clean a stale v_path from a vnode. This is only performed if the v_path has - * not been altered since it was found to be stale - */ - static void - vnode_clear_vpath(vnode_t *vp, char *vpath_old) - { - mutex_enter(&vp->v_lock); - if (vp->v_path != vn_vpath_empty && vp->v_path == vpath_old) { - vp->v_path = vn_vpath_empty; - mutex_exit(&vp->v_lock); - kmem_free(vpath_old, strlen(vpath_old) + 1); - } else { - mutex_exit(&vp->v_lock); - } - } - - /* - * Validate that a pathname refers to a given vnode. - */ - static int - vnode_valid_pn(vnode_t *vp, vnode_t *vrootp, pathname_t *pn, pathname_t *rpn, - int flags, cred_t *cr) - { - vnode_t *compvp; - /* - * If we are in a zone or a chroot environment, then we have to - * take additional steps, since the path to the root might not - * be readable with the current credentials, even though the - * process can legitmately access the file. In this case, we - * do the following: - * - * lookuppnvp() with all privileges to get the resolved path. - * call localpath() to get the local portion of the path, and - * continue as normal. - * - * If the the conversion to a local path fails, then we continue - * as normal. This is a heuristic to make process object file - * paths available from within a zone. Because lofs doesn't - * support page operations, the vnode stored in the seg_t is - * actually the underlying real vnode, not the lofs node itself. - * Most of the time, the lofs path is the same as the underlying - * vnode (for example, /usr/lib/libc.so.1). - */ - if (vrootp != rootdir) { - char *local = NULL; - - VN_HOLD(rootdir); - if (lookuppnvp(pn, rpn, FOLLOW, NULL, &compvp, rootdir, - rootdir, kcred) == 0) { - local = localpath(rpn->pn_path, vrootp, kcred); - VN_RELE(compvp); - } - - /* - * The original pn was changed through lookuppnvp(). - * Set it to local for next validation attempt. - */ - if (local) { - (void) pn_set(pn, local); - } else { - return (1); - } - } - - /* - * We should have a local path at this point, so start the search from - * the root of the current process. - */ - VN_HOLD(vrootp); - if (vrootp != rootdir) - VN_HOLD(vrootp); - if (lookuppnvp(pn, rpn, FOLLOW | flags, NULL, &compvp, vrootp, vrootp, - cr) == 0) { - /* - * Check to see if the returned vnode is the same as the one we - * expect. - */ - if (vn_compare(vp, compvp) || - vnode_match(vp, compvp, cr)) { - VN_RELE(compvp); - return (0); - } else { - VN_RELE(compvp); - } - } - - return (1); - } - - /* - * Struct for tracking vnodes with invalidated v_path entries during a - * dirtopath reverse lookup. By keepeing adequate state, those vnode can be - * revisted to populate v_path. - */ - struct dirpath_walk { - struct dirpath_walk *dw_next; - vnode_t *dw_vnode; - vnode_t *dw_pvnode; - size_t dw_len; - char *dw_name; - }; - - /* * Given a directory, return the full, resolved path. This looks up "..", * searches for the given vnode in the parent, appends the component, etc. It * is used to implement vnodetopath() and getcwd() when the cached path fails. */ static int dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags, cred_t *cr) { pathname_t pn, rpn, emptypn; ! vnode_t *pvp = NULL, *startvp = vp; ! int err = 0; size_t complen; dirent64_t *dp; ! char *bufloc, *dbuf; ! const size_t dlen = DIRENT64_RECLEN(MAXPATHLEN); ! struct dirpath_walk *dw_chain = NULL, *dw_entry; /* Operation only allowed on directories */ ASSERT(vp->v_type == VDIR); /* We must have at least enough space for "/" */ --- 977,1004 ---- return (ret); } /* * Given a directory, return the full, resolved path. This looks up "..", * searches for the given vnode in the parent, appends the component, etc. It * is used to implement vnodetopath() and getcwd() when the cached path fails. */ static int dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags, cred_t *cr) { pathname_t pn, rpn, emptypn; ! vnode_t *cmpvp, *pvp = NULL; ! vnode_t *startvp = vp; ! int err = 0, vprivs; size_t complen; + char *dbuf; dirent64_t *dp; ! char *bufloc; ! size_t dlen = DIRENT64_RECLEN(MAXPATHLEN); ! refstr_t *mntpt; /* Operation only allowed on directories */ ASSERT(vp->v_type == VDIR); /* We must have at least enough space for "/" */
*** 1120,1132 **** * during the loop. */ VN_HOLD(vp); for (;;) { - int vprivs; - hrtime_t cached_stamp; - /* * Return if we've reached the root. If the buffer is empty, * return '/'. We explicitly don't use vn_compare(), since it * compares the real vnodes. A lofs mount of '/' would produce * incorrect results otherwise. --- 1019,1028 ----
*** 1147,1187 **** err = EPERM; goto out; } /* * Shortcut: see if this vnode has correct v_path. If so, * we have the work done. */ mutex_enter(&vp->v_lock); ! if (vp->v_path != vn_vpath_empty && ! pn_set(&pn, vp->v_path) == 0) { ! cached_stamp = vp->v_path_stamp; mutex_exit(&vp->v_lock); rpn.pn_path = rpn.pn_buf; ! /* Ensure the v_path pointing to correct vnode */ ! if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags, ! cr) == 0) { complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } ! bcopy(rpn.pn_path, bufloc, complen); break; } else { ! /* ! * Immediately nuke cached v_path entries known ! * to be invalid. ! */ ! vn_clearpath(vp, cached_stamp); } } else { mutex_exit(&vp->v_lock); } /* * Shortcuts failed, search for this vnode in its parent. If * this is a mountpoint, then get the vnode underneath. */ --- 1043,1135 ---- err = EPERM; goto out; } /* + * Shortcut: see if this vnode is a mountpoint. If so, + * grab the path information from the vfs_t. + */ + if (vp->v_flag & VROOT) { + + mntpt = vfs_getmntpoint(vp->v_vfsp); + if ((err = pn_set(&pn, (char *)refstr_value(mntpt))) + == 0) { + refstr_rele(mntpt); + rpn.pn_path = rpn.pn_buf; + + /* + * Ensure the mountpoint still exists. + */ + VN_HOLD(vrootp); + if (vrootp != rootdir) + VN_HOLD(vrootp); + if (lookuppnvp(&pn, &rpn, flags, NULL, + &cmpvp, vrootp, vrootp, cr) == 0) { + + if (VN_CMP(vp, cmpvp)) { + VN_RELE(cmpvp); + + complen = strlen(rpn.pn_path); + bufloc -= complen; + if (bufloc < buf) { + err = ERANGE; + goto out; + } + bcopy(rpn.pn_path, bufloc, + complen); + break; + } else { + VN_RELE(cmpvp); + } + } + } else { + refstr_rele(mntpt); + } + } + + /* * Shortcut: see if this vnode has correct v_path. If so, * we have the work done. */ mutex_enter(&vp->v_lock); ! if (vp->v_path != NULL) { ! ! if ((err = pn_set(&pn, vp->v_path)) == 0) { mutex_exit(&vp->v_lock); rpn.pn_path = rpn.pn_buf; ! /* ! * Ensure the v_path pointing to correct vnode ! */ ! VN_HOLD(vrootp); ! if (vrootp != rootdir) ! VN_HOLD(vrootp); ! if (lookuppnvp(&pn, &rpn, flags, NULL, ! &cmpvp, vrootp, vrootp, cr) == 0) { ! ! if (VN_CMP(vp, cmpvp)) { ! VN_RELE(cmpvp); ! complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } ! bcopy(rpn.pn_path, bufloc, ! complen); break; } else { ! VN_RELE(cmpvp); } + } } else { mutex_exit(&vp->v_lock); } + } else { + mutex_exit(&vp->v_lock); + } /* * Shortcuts failed, search for this vnode in its parent. If * this is a mountpoint, then get the vnode underneath. */
*** 1218,1227 **** --- 1166,1207 ---- if ((err = VOP_ACCESS(pvp, vprivs, 0, cr, NULL)) != 0) { goto out; } /* + * Try to obtain the path component from dnlc cache + * before searching through the directory. + */ + if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) { + /* + * If we got parent vnode as a result, + * then the answered path is correct. + */ + if (VN_CMP(cmpvp, pvp)) { + VN_RELE(cmpvp); + complen = strlen(dbuf); + bufloc -= complen; + if (bufloc <= buf) { + err = ENAMETOOLONG; + goto out; + } + bcopy(dbuf, bufloc, complen); + + /* Prepend a slash to the current path */ + *--bufloc = '/'; + + /* And continue with the next component */ + VN_RELE(vp); + vp = pvp; + pvp = NULL; + continue; + } else { + VN_RELE(cmpvp); + } + } + + /* * Search the parent directory for the entry corresponding to * this vnode. */ if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp)) != 0)
*** 1235,1257 **** bcopy(dp->d_name, bufloc, complen); /* Prepend a slash to the current path. */ *--bufloc = '/'; - /* - * Record the name and directory for later reconstruction and - * link it up with the others. - */ - dw_entry = kmem_alloc(sizeof (*dw_entry), KM_SLEEP); - dw_entry->dw_name = kmem_alloc(complen + 1, KM_SLEEP); - VN_HOLD(dw_entry->dw_vnode = vp); - VN_HOLD(dw_entry->dw_pvnode = pvp); - bcopy(dp->d_name, dw_entry->dw_name, complen + 1); - dw_entry->dw_len = complen; - dw_entry->dw_next = dw_chain; - dw_chain = dw_entry; - /* And continue with the next component */ VN_RELE(vp); vp = pvp; pvp = NULL; } --- 1215,1224 ----
*** 1262,1302 **** if (bufloc != buf) ovbcopy(bufloc, buf, buflen - (bufloc - buf)); out: /* - * Walk over encountered directory entries which were afflicted with a - * stale or absent v_path. If the dirtopath was successful, we should - * possess the necessary information to populate all of them with a - * valid v_path. - * - * While processing this list, it is safe to call vn_setpath despite - * the fact that racing vnode actions may have altered v_path entries - * while the above loopwas still executing. Any updated entries will - * have a newer v_path_stamp value which prevents an invalid overwrite. - * - * If an error was encountered during the search, freeing the chain is - * still required. - */ - dw_entry = dw_chain; - while (dw_entry != NULL) { - struct dirpath_walk *next = dw_entry->dw_next; - - if (err == 0) { - vn_setpath(NULL, dw_entry->dw_pvnode, - dw_entry->dw_vnode, dw_entry->dw_name, - dw_entry->dw_len); - } - - VN_RELE(dw_entry->dw_vnode); - VN_RELE(dw_entry->dw_pvnode); - kmem_free(dw_entry->dw_name, dw_entry->dw_len + 1); - kmem_free(dw_entry, sizeof (*dw_entry)); - dw_entry = next; - } - - /* * If the error was ESTALE and the current directory to look in * was the root for this lookup, the root for a mounted file * system, or the starting directory for lookups, then * return ENOENT instead of ESTALE. In this case, no recovery * is possible by the higher level. If ESTALE was returned for --- 1229,1238 ----
*** 1332,1353 **** */ static int vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr, int flags) { ! pathname_t pn; ! int ret = 0; ! vnode_t *realvp; ! boolean_t doclose = B_FALSE; /* * If vrootp is NULL, get the root for curproc. Callers with any other * requirements should pass in a different vrootp. */ if (vrootp == NULL) { - proc_t *p = curproc; - mutex_enter(&p->p_lock); if ((vrootp = PTOU(p)->u_rdir) == NULL) vrootp = rootdir; VN_HOLD(vrootp); mutex_exit(&p->p_lock); --- 1268,1289 ---- */ static int vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr, int flags) { ! pathname_t pn, rpn; ! int ret, len; ! vnode_t *compvp, *pvp, *realvp; ! proc_t *p = curproc; ! char path[MAXNAMELEN]; ! int doclose = 0; /* * If vrootp is NULL, get the root for curproc. Callers with any other * requirements should pass in a different vrootp. */ if (vrootp == NULL) { mutex_enter(&p->p_lock); if ((vrootp = PTOU(p)->u_rdir) == NULL) vrootp = rootdir; VN_HOLD(vrootp); mutex_exit(&p->p_lock);
*** 1365,1427 **** */ if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 && realvp != vp) { VN_HOLD(vp); if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0) ! doclose = B_TRUE; else VN_RELE(vp); } /* ! * Check to see if we have a valid cached path in the vnode. */ - pn_alloc(&pn); mutex_enter(&vp->v_lock); ! if (vp->v_path != vn_vpath_empty) { ! hrtime_t cached_stamp; ! pathname_t rpn; ! ! cached_stamp = vp->v_path_stamp; (void) pn_set(&pn, vp->v_path); mutex_exit(&vp->v_lock); /* We should only cache absolute paths */ ASSERT(pn.pn_buf[0] == '/'); ! pn_alloc(&rpn); ! if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags, cr) == 0) { ! /* Return the result, if we're able. */ ! if (buflen > rpn.pn_pathlen) { ! bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1); } else { ! ret = ENAMETOOLONG; } pn_free(&pn); pn_free(&rpn); ! goto out; } pn_free(&rpn); - vn_clearpath(vp, cached_stamp); } else { mutex_exit(&vp->v_lock); } pn_free(&pn); ! if (vp->v_type != VDIR) { /* ! * The reverse lookup tricks used by dirtopath aren't possible ! * for non-directory entries. The best which can be done is ! * clearing any stale v_path so later lookups can potentially ! * repopulate it with a valid path. */ ret = ENOENT; } else { ! ret = dirtopath(vrootp, vp, buf, buflen, flags, cr); } ! out: VN_RELE(vrootp); if (doclose) { (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL); VN_RELE(vp); } --- 1301,1467 ---- */ if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 && realvp != vp) { VN_HOLD(vp); if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0) ! doclose = 1; else VN_RELE(vp); } + pn_alloc(&pn); + /* ! * Check to see if we have a cached path in the vnode. */ mutex_enter(&vp->v_lock); ! if (vp->v_path != NULL) { (void) pn_set(&pn, vp->v_path); mutex_exit(&vp->v_lock); + pn_alloc(&rpn); + /* We should only cache absolute paths */ ASSERT(pn.pn_buf[0] == '/'); ! /* ! * If we are in a zone or a chroot environment, then we have to ! * take additional steps, since the path to the root might not ! * be readable with the current credentials, even though the ! * process can legitmately access the file. In this case, we ! * do the following: ! * ! * lookuppnvp() with all privileges to get the resolved path. ! * call localpath() to get the local portion of the path, and ! * continue as normal. ! * ! * If the the conversion to a local path fails, then we continue ! * as normal. This is a heuristic to make process object file ! * paths available from within a zone. Because lofs doesn't ! * support page operations, the vnode stored in the seg_t is ! * actually the underlying real vnode, not the lofs node itself. ! * Most of the time, the lofs path is the same as the underlying ! * vnode (for example, /usr/lib/libc.so.1). ! */ ! if (vrootp != rootdir) { ! char *local = NULL; ! VN_HOLD(rootdir); ! if (lookuppnvp(&pn, &rpn, FOLLOW, ! NULL, &compvp, rootdir, rootdir, kcred) == 0) { ! local = localpath(rpn.pn_path, vrootp, ! kcred); ! VN_RELE(compvp); ! } ! ! /* ! * The original pn was changed through lookuppnvp(). ! * Set it to local for next validation attempt. ! */ ! if (local) { ! (void) pn_set(&pn, local); } else { ! goto notcached; } + } + + /* + * We should have a local path at this point, so start the + * search from the root of the current process. + */ + VN_HOLD(vrootp); + if (vrootp != rootdir) + VN_HOLD(vrootp); + ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL, + &compvp, vrootp, vrootp, cr); + if (ret == 0) { + /* + * Check to see if the returned vnode is the same as + * the one we expect. If not, give up. + */ + if (!vn_compare(vp, compvp) && + !vnode_match(vp, compvp, cr)) { + VN_RELE(compvp); + goto notcached; + } + + VN_RELE(compvp); + + /* + * Return the result. + */ + if (buflen <= rpn.pn_pathlen) + goto notcached; + + bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1); pn_free(&pn); pn_free(&rpn); ! VN_RELE(vrootp); ! if (doclose) { ! (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL); ! VN_RELE(vp); } + return (0); + } + + notcached: pn_free(&rpn); } else { mutex_exit(&vp->v_lock); } + pn_free(&pn); ! if (PROC_IS_BRANDED(curproc)) { /* ! * If v_path doesn't work out and we're in a branded zone, ! * we're not going to bother doing more work here: because ! * directories from the global can be lofs mounted into odd ! * locations (e.g., /native in an lx zone), it is likely that ! * the DNLC reverse lookup will yield nothing. Indeed, the ! * only certainty is that the DNLC reverse lookup will be ! * exceedingly painful; we save ourselves the substantial ! * grief of scanning the entire DNLC and kick out with ENOENT ! * in this case. */ ret = ENOENT; + } else if (vp->v_type != VDIR) { + /* + * If we don't have a directory, try to find it in the dnlc via + * reverse lookup. Once this is found, we can use the regular + * directory search to find the full path. + */ + if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) { + /* + * Check if we have read privilege so, that + * we can lookup the path in the directory + */ + ret = 0; + if ((flags & LOOKUP_CHECKREAD)) { + ret = VOP_ACCESS(pvp, VREAD, 0, cr, NULL); + } + if (ret == 0) { + ret = dirtopath(vrootp, pvp, buf, buflen, + flags, cr); + } + if (ret == 0) { + len = strlen(buf); + if (len + strlen(path) + 1 >= buflen) { + ret = ENAMETOOLONG; } else { ! if (buf[len - 1] != '/') ! buf[len++] = '/'; ! bcopy(path, buf + len, ! strlen(path) + 1); } + } ! VN_RELE(pvp); ! } else ! ret = ENOENT; ! } else ! ret = dirtopath(vrootp, vp, buf, buflen, flags, cr); ! VN_RELE(vrootp); if (doclose) { (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL); VN_RELE(vp); }