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);
}