summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/common/Pzone.c
diff options
context:
space:
mode:
authorEdward Pilatowicz <Edward.Pilatowicz@Sun.COM>2008-09-23 22:32:10 -0700
committerEdward Pilatowicz <Edward.Pilatowicz@Sun.COM>2008-09-23 22:32:10 -0700
commit186f7fbf5e07d046b50e4e15c32b21f109b76c80 (patch)
tree7bb0d0b3f1656c4959df4535c7adc296422b5863 /usr/src/lib/libproc/common/Pzone.c
parentfeccaf6df4d61f3e7e0723a768ce407f9eb6a1dd (diff)
downloadillumos-joyent-186f7fbf5e07d046b50e4e15c32b21f109b76c80.tar.gz
PSARC/2008/490 pmadvise/pldd unresolved link map flag
6599704 libproc should look inside zones for objects --HG-- rename : usr/src/lib/libproc/common/Pbrand.c => usr/src/lib/libproc/common/Pzone.c
Diffstat (limited to 'usr/src/lib/libproc/common/Pzone.c')
-rw-r--r--usr/src/lib/libproc/common/Pzone.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/Pzone.c b/usr/src/lib/libproc/common/Pzone.c
new file mode 100644
index 0000000000..c4023c2301
--- /dev/null
+++ b/usr/src/lib/libproc/common/Pzone.c
@@ -0,0 +1,748 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <libzonecfg.h>
+#include <link.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/list.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/mman.h>
+#include <sys/mnttab.h>
+
+#include "Pcontrol.h"
+
+struct path_node {
+ struct path_node *pn_next;
+ char *pn_path;
+};
+typedef struct path_node path_node_t;
+
+static path_node_t *
+pn_push(path_node_t **pnp, char *path)
+{
+ path_node_t *pn;
+
+ if ((pn = calloc(sizeof (path_node_t), 1)) == NULL)
+ return (NULL);
+
+ if ((pn->pn_path = strdup(path)) == NULL) {
+ free(pn);
+ return (NULL);
+ }
+ pn->pn_next = *pnp;
+ return (*pnp = pn);
+}
+
+static void
+pn_free(path_node_t **pnp)
+{
+ path_node_t *pn;
+
+ while (*pnp != NULL) {
+ pn = *pnp;
+ *pnp = pn->pn_next;
+ free(pn->pn_path);
+ free(pn);
+ }
+}
+
+static void
+pn_free2(path_node_t **pn1, path_node_t **pn2)
+{
+ pn_free(pn1);
+ pn_free(pn2);
+}
+
+static char *
+pn_pop(path_node_t **pnp, char *path)
+{
+ path_node_t *pn;
+
+ if (*pnp == NULL)
+ return (NULL);
+
+ pn = *pnp;
+ *pnp = pn->pn_next;
+ pn->pn_next = NULL;
+
+ if (path == NULL) {
+ pn_free(&pn);
+ return (NULL);
+ }
+ (void) strlcpy(path, pn->pn_path, PATH_MAX);
+ pn_free(&pn);
+ return (path);
+}
+
+
+/*
+ * Libzonecfg.so links against libproc, so libproc can't link against
+ * libzonecfg.so. Also, libzonecfg.so is optional and might not be
+ * installed. Hence instead of relying on linking to access libzonecfg.so,
+ * we'll try dlopening it here. This trick is borrowed from
+ * libc`zone_get_id(), see that function for more detailed comments.
+ */
+static int
+i_zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
+{
+ typedef int (*zone_get_zonepath_t)(char *, char *, size_t);
+ static zone_get_zonepath_t zone_get_zonepath_fp = NULL;
+
+ if (zone_get_zonepath_fp == NULL) {
+ /* There's no harm in doing this multiple times. */
+ void *dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY);
+ void *sym = (void *)(-1);
+ if (dlhandle != NULL &&
+ (sym = dlsym(dlhandle, "zone_get_zonepath")) == NULL) {
+ sym = (void *)(-1);
+ (void) dlclose(dlhandle);
+ }
+ zone_get_zonepath_fp = (zone_get_zonepath_t)sym;
+ }
+
+ /* If we've successfully loaded it, call the real function */
+ if (zone_get_zonepath_fp != (zone_get_zonepath_t)(-1))
+ return (zone_get_zonepath_fp(zone_name, zonepath, rp_sz));
+ return (Z_NO_ZONE);
+}
+
+char *
+Pbrandname(struct ps_prochandle *P, char *buf, size_t buflen)
+{
+ long addr;
+
+ if ((addr = Pgetauxval(P, AT_SUN_BRANDNAME)) == -1)
+ return (NULL);
+
+ if (Pread_string(P, buf, buflen, addr) == -1)
+ return (NULL);
+
+ return (buf);
+}
+
+/*
+ * Get the zone name from the core file if we have it; look up the
+ * name based on the zone id if this is a live process.
+ */
+char *
+Pzonename(struct ps_prochandle *P, char *s, size_t n)
+{
+ if (P->state == PS_IDLE) {
+ errno = ENODATA;
+ return (NULL);
+ }
+
+ if (P->state == PS_DEAD) {
+ if (P->core->core_zonename == NULL) {
+ errno = ENODATA;
+ return (NULL);
+ }
+ (void) strlcpy(s, P->core->core_zonename, n);
+ } else {
+ if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0)
+ return (NULL);
+ s[n - 1] = '\0';
+ }
+ return (s);
+}
+
+char *
+Pzoneroot(struct ps_prochandle *P, char *s, size_t n)
+{
+ char zname[ZONENAME_MAX], zpath[PATH_MAX], tmp[PATH_MAX];
+ int rv;
+
+ if (P->zoneroot != NULL) {
+ (void) strlcpy(s, P->zoneroot, n);
+ return (s);
+ }
+
+ if ((Pzonename(P, zname, sizeof (zname)) == NULL) ||
+ (strcmp(zname, GLOBAL_ZONENAME) == 0)) {
+ if ((P->zoneroot = strdup("")) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ dprintf("Pzoneroot defaulting to '%s'\n", GLOBAL_ZONENAME);
+ (void) strlcpy(s, P->zoneroot, n);
+ return (s);
+ }
+
+ if (i_zone_get_zonepath(zname, zpath, sizeof (zpath)) != Z_OK) {
+ if ((P->zoneroot = strdup("")) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ dprintf(
+ "Pzoneroot zone not found '%s', defaulting to '%s'\n",
+ zname, GLOBAL_ZONENAME);
+ (void) strlcpy(s, P->zoneroot, n);
+ return (s);
+ }
+ (void) strlcat(zpath, "/root", sizeof (zpath));
+
+ if ((rv = resolvepath(zpath, tmp, sizeof (tmp) - 1)) < 0) {
+ if ((P->zoneroot = strdup("")) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ dprintf(
+ "Pzoneroot can't access '%s:%s', defaulting to '%s'\n",
+ zname, zpath, GLOBAL_ZONENAME);
+ (void) strlcpy(s, P->zoneroot, n);
+ return (s);
+ }
+ tmp[rv] = '\0';
+ (void) strlcpy(zpath, tmp, sizeof (zpath));
+
+ if ((P->zoneroot = strdup(zpath)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ dprintf("Pzoneroot found zone root '%s:%s'\n", zname, zpath);
+ (void) strlcpy(s, P->zoneroot, n);
+ return (s);
+}
+
+/*
+ * Plofspath() takes a path, "path", and removes any lofs components from
+ * that path. The resultant path (if different from the starting path)
+ * is placed in "s", which is limited to "n" characters, and the return
+ * value is the pointer s. If there are no lofs components in the path
+ * the NULL is returned and s is not modified. It's ok for "path" and
+ * "s" to be the same pointer. (ie, the results can be stored directly
+ * in the input buffer.) The path that is passed in must be an absolute
+ * path.
+ *
+ * Example:
+ * if "path" == "/foo/bar", and "/candy/" is lofs mounted on "/foo/"
+ * then "/candy/bar/" will be written into "s" and "s" will be returned.
+ */
+char *
+Plofspath(const char *path, char *s, size_t n)
+{
+ char tmp[PATH_MAX + 1];
+ struct mnttab mt, mt_find;
+ FILE *fp;
+ char *p, *p2;
+ int rv;
+
+ dprintf("Plofspath path '%s'\n", path);
+
+ /* We only deal with absolute paths */
+ if (path[0] != '/')
+ return (NULL);
+
+ /* Open /etc/mnttab */
+ if ((fp = fopen(MNTTAB, "r")) == NULL)
+ return (NULL);
+
+ /* Make a copy of the path so that we can muck with it */
+ (void) strlcpy(tmp, path, sizeof (tmp) - 1);
+
+ /*
+ * Use resolvepath() to make sure there are no consecutive or
+ * trailing '/'s in the path.
+ */
+ if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
+ tmp[rv] = '\0';
+
+ /*
+ * So now we're going to search the path for any components that
+ * might be lofs mounts. We'll start out search from the full
+ * path and then step back through each parent directly till
+ * we reach the root. If we find a lofs mount point in the path
+ * then we'll replace the initial portion of the path (up
+ * to that mount point) with the source of that mount point
+ * and then start our search over again.
+ *
+ * Here's some of the variables we're going to use:
+ *
+ * tmp - A pointer to our working copy of the path. Sometimes
+ * this path will be divided into two strings by a
+ * '\0' (NUL) character. The first string is the
+ * component we're currently checking and the second
+ * string is the path components we've already checked.
+ *
+ * p - A pointer to the last '/' seen in the string.
+ *
+ * p[1] - A pointer to the component of the string we've already
+ * checked.
+ *
+ * Initially, p will point to the end of our path and p[1] will point
+ * to an extra '\0' (NUL) that we'll append to the end of the string.
+ * (This is why we declared tmp with a size of PATH_MAX + 1).
+ */
+ p = &tmp[strlen(tmp)];
+ p[1] = '\0';
+ for (;;) {
+ /* Check if tmp is a mount point */
+ rewind(fp);
+ bzero(&mt_find, sizeof (mt_find));
+ mt_find.mnt_mountp = tmp;
+ rv = getmntany(fp, &mt, &mt_find);
+
+ /* We only care about lofs mount points */
+ if ((rv == 0) && (strcmp(mt.mnt_fstype, "lofs") == 0)) {
+ char tmp2[PATH_MAX + 1];
+
+ /*
+ * We found a lofs mount. Update the path that we're
+ * checking and start over. This means append the
+ * portion of the path we've already checked to the
+ * source of the lofs mount and re-start this entire
+ * lofs resolution loop. Use resolvepath() to make
+ * sure there are no consecutive or trailing '/'s
+ * in the path.
+ */
+ (void) strlcpy(tmp2, mt.mnt_special, sizeof (tmp2) - 1);
+ (void) strlcat(tmp2, "/", sizeof (tmp2) - 1);
+ (void) strlcat(tmp2, &p[1], sizeof (tmp2) - 1);
+ (void) strlcpy(tmp, tmp2, sizeof (tmp) - 1);
+ if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
+ tmp[rv] = '\0';
+ p = &tmp[strlen(tmp)];
+ p[1] = '\0';
+ continue;
+ }
+
+ /* No lofs mount found */
+ if ((p2 = strrchr(tmp, '/')) == NULL) {
+ char tmp2[PATH_MAX];
+
+ /*
+ * We know that tmp was an absolute path, so if we
+ * made it here we know that (p == tmp) and that
+ * (*p == '\0'). This means that we've managed
+ * to check the whole path and so we're done.
+ */
+ assert(p == tmp);
+ assert(p[0] == '\0');
+ (void) fclose(fp);
+
+ /* Restore the leading '/' in the path */
+ p[0] = '/';
+
+ if (strcmp(tmp, path) == 0) {
+ /* The path didn't change */
+ return (NULL);
+ }
+
+ /*
+ * It's possible that lofs source path we just
+ * obtained contains a symbolic link. Use
+ * resolvepath() to clean it up.
+ */
+ (void) strlcpy(tmp2, tmp, sizeof (tmp2));
+ if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
+ tmp[rv] = '\0';
+
+ /*
+ * It's always possible that our lofs source path is
+ * actually another lofs mount. So call ourselves
+ * recursively to resolve that path.
+ */
+ (void) Plofspath(tmp, tmp, PATH_MAX);
+
+ /* Copy out our final resolved lofs source path */
+ (void) strlcpy(s, tmp, n);
+ dprintf("Plofspath path result '%s'\n", s);
+ return (s);
+ }
+
+ /*
+ * So the path we just checked is not a lofs mount. Next we
+ * want to check the parent path component for a lofs mount.
+ *
+ * First, restore any '/' that we replaced with a '\0' (NUL).
+ * We can determine if we should do this by looking at p[1].
+ * If p[1] points to a '\0' (NUL) then we know that p points
+ * to the end of the string and there is no '/' to restore.
+ * if p[1] doesn't point to a '\0' (NUL) then it points to
+ * the part of the path that we've already verified so there
+ * is a '/' to restore.
+ */
+ if (p[1] != '\0')
+ p[0] = '/';
+
+ /*
+ * Second, replace the last '/' in the part of the path
+ * that we've already checked with a '\0' (NUL) so that
+ * when we loop around we check the parent component of the
+ * path.
+ */
+ p2[0] = '\0';
+ p = p2;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Pzonepath() - Way too much code to attempt to derive the full path of
+ * an object within a zone.
+ *
+ * Pzonepath() takes a path and attempts to resolve it relative to the
+ * root associated with the current process handle. If it fails it will
+ * not update the results string. It is safe to specify the same pointer
+ * for the file string and the results string.
+ *
+ * Doing this resolution is more difficult than it initially sounds.
+ * We can't simply append the file path to the zone root, because in
+ * a root directory, '..' is treated the same as '.'. Also, symbolic
+ * links that specify an absolute path need to be interpreted relative
+ * to the zone root.
+ *
+ * It seems like perhaps we could do a chroot(<zone root>) followed by a
+ * resolvepath(). But we can't do this because chroot requires special
+ * privileges and affects the entire process. Perhaps if there was a
+ * special version of resolvepath() which took an addition root path
+ * we could use that, but this isn't ideal either. The reason is
+ * that we want to have special handling for native paths. (A native path
+ * is a path that begins with "/native/" or "/.SUNWnative/".) Native
+ * paths could be passed explicity to this function or could be embedded
+ * in a symlink that is part of the path passed into this function.
+ * These paths are always lofs mounts of global zone paths, but lofs
+ * mounts only exist when a zone is booted. So if we were to try to do
+ * a resolvepath() on a native path when the zone wasn't booted the
+ * resolvepath() would fail even though we know that the components
+ * exists in the global zone.
+ *
+ * Given all these constraints, we just implement a path walking function
+ * that resolves a file path relative to a zone root by manually inspecting
+ * each of the path components and verifying its existence. This means that
+ * we must have access to the zone and that all the components of the
+ * path must exist for this operation to succeed.
+ */
+char *
+Pzonepath(struct ps_prochandle *P, const char *path, char *s, size_t n)
+{
+ char zroot[PATH_MAX], zpath[PATH_MAX], tmp[PATH_MAX], link[PATH_MAX];
+ path_node_t *pn_stack = NULL, *pn_links = NULL, *pn;
+ struct stat64 sb;
+ char *p;
+ int i, rv;
+
+ dprintf("Pzonepath lookup '%s'\n", path);
+
+ /* First lookup the zone root */
+ if (Pzoneroot(P, zroot, sizeof (zroot)) == NULL)
+ return (NULL);
+
+ /*
+ * Make a temporary copy of the path specified.
+ * If it's a relative path then make it into an absolute path.
+ */
+ tmp[0] = '\0';
+ if (path[0] != '/')
+ (void) strlcat(tmp, "/", sizeof (tmp));
+ (void) strlcat(tmp, path, sizeof (tmp));
+
+ /*
+ * If the path that was passed in is the zone root, we're done.
+ * If the path that was passed in already contains the zone root
+ * then strip the zone root out and verify the rest of the path.
+ */
+ if (strcmp(tmp, zroot) == 0) {
+ (void) Plofspath(zroot, zroot, sizeof (zroot));
+ dprintf("Pzonepath found zone path (1) '%s'\n", zroot);
+ (void) strlcpy(s, zroot, n);
+ return (s);
+ }
+ i = strlen(zroot);
+ if ((strncmp(tmp, zroot, i) == 0) && (tmp[i] == '/'))
+ (void) memmove(tmp, tmp + i, strlen(tmp + i) + 1);
+
+ /* If no path is passed in, then it maps to the zone root */
+ if (strlen(tmp) == 0) {
+ (void) Plofspath(zroot, zroot, sizeof (zroot));
+ dprintf("Pzonepath found zone path (2) '%s'\n", zroot);
+ (void) strlcpy(s, zroot, n);
+ return (s);
+ }
+
+ /*
+ * Push each path component that we plan to verify onto a stack of
+ * path components, with parent components at the top of the stack.
+ * So for example, if we're going to verify the path /foo/bar/bang
+ * then our stack will look like:
+ * foo (top)
+ * bar
+ * bang (bottom)
+ */
+ while ((p = strrchr(tmp, '/')) != NULL) {
+ *p = '\0';
+ if (pn_push(&pn_stack, &p[1]) != NULL)
+ continue;
+ pn_free(&pn_stack);
+ return (NULL);
+ }
+
+ /* We're going to store the final zone relative path in zpath */
+ *zpath = '\0';
+
+ while (pn_pop(&pn_stack, tmp) != NULL) {
+ /*
+ * Drop zero length path components (which come from
+ * consecutive '/'s) and '.' path components.
+ */
+ if ((strlen(tmp) == 0) || (strcmp(tmp, ".") == 0))
+ continue;
+
+ /*
+ * Check the current path component for '..', if found
+ * drop any previous path component.
+ */
+ if (strcmp(tmp, "..") == 0) {
+ if ((p = strrchr(zpath, '/')) != NULL)
+ *p = '\0';
+ continue;
+ }
+
+ /* The path we want to verify now is zpath + / + tmp. */
+ (void) strlcat(zpath, "/", sizeof (zpath));
+ (void) strlcat(zpath, tmp, sizeof (zpath));
+
+ /*
+ * Check if this is a native object. A native object is an
+ * object from the global zone that is running in a branded
+ * zone. These objects are lofs mounted into a zone. So if a
+ * branded zone is not booted then lofs mounts won't be setup
+ * so we won't be able to find these objects. Luckily, we know
+ * that they exist in the global zone with the same path sans
+ * the initial native component, so we'll just strip out the
+ * native component here.
+ */
+ if ((strncmp(zpath, "/native", sizeof ("/native")) == 0) ||
+ (strncmp(zpath, "/.SUNWnative",
+ sizeof ("/.SUNWnative")) == 0)) {
+
+ /* Free any cached symlink paths */
+ pn_free(&pn_links);
+
+ /* Reconstruct the path from our path component stack */
+ *zpath = '\0';
+ while (pn_pop(&pn_stack, tmp) != NULL) {
+ (void) strlcat(zpath, "/", sizeof (zpath));
+ (void) strlcat(zpath, tmp, sizeof (zpath));
+ }
+
+ /* Verify that the path actually exists */
+ rv = resolvepath(zpath, tmp, sizeof (tmp) - 1);
+ if (rv < 0) {
+ dprintf("Pzonepath invalid native path '%s'\n",
+ zpath);
+ return (NULL);
+ }
+ tmp[rv] = '\0';
+
+ /* Return the path */
+ dprintf("Pzonepath found native path '%s'\n", tmp);
+ (void) Plofspath(tmp, tmp, sizeof (tmp));
+ (void) strlcpy(s, tmp, n);
+ return (s);
+ }
+
+ /*
+ * Check if the path points to a symlink. We do this
+ * explicitly since any absolute symlink needs to be
+ * interpreted relativly to the zone root and not "/".
+ */
+ (void) strlcpy(tmp, zroot, sizeof (tmp));
+ (void) strlcat(tmp, zpath, sizeof (tmp));
+ if (lstat64(tmp, &sb) != 0) {
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+ if (!S_ISLNK(sb.st_mode)) {
+ /*
+ * Since the lstat64() above succeeded we know that
+ * zpath exists, since this is not a symlink loop
+ * around and check the next path component.
+ */
+ continue;
+ }
+
+ /*
+ * Symlink allow for paths with loops. Make sure
+ * we're not stuck in a loop.
+ */
+ for (pn = pn_links; pn != NULL; pn = pn->pn_next) {
+ if (strcmp(zpath, pn->pn_path) != 0)
+ continue;
+
+ /* We have a loop. Fail. */
+ dprintf("Pzonepath symlink loop '%s'\n", zpath);
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+
+ /* Save this symlink path for future loop checks */
+ if (pn_push(&pn_links, zpath) == NULL) {
+ /* Out of memory */
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+
+ /* Now follow the contents of the symlink */
+ bzero(link, sizeof (link));
+ if (readlink(tmp, link, sizeof (link)) == -1) {
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+
+ dprintf("Pzonepath following symlink '%s' -> '%s'\n",
+ zpath, link);
+
+ /*
+ * Push each path component of the symlink target onto our
+ * path components stack since we need to verify each one.
+ */
+ while ((p = strrchr(link, '/')) != NULL) {
+ *p = '\0';
+ if (pn_push(&pn_stack, &p[1]) != NULL)
+ continue;
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+
+ /* absolute or relative symlink? */
+ if (*link == '\0') {
+ /* Absolute symlink, nuke existing zpath. */
+ *zpath = '\0';
+ continue;
+ }
+
+ /*
+ * Relative symlink. Push the first path component of the
+ * symlink target onto our stack for verification and then
+ * remove the current path component from zpath.
+ */
+ if (pn_push(&pn_stack, link) == NULL) {
+ pn_free2(&pn_stack, &pn_links);
+ return (NULL);
+ }
+ p = strrchr(zpath, '/');
+ assert(p != NULL);
+ *p = '\0';
+ continue;
+ }
+ pn_free(&pn_links);
+
+ /* Place the final result in zpath */
+ (void) strlcpy(tmp, zroot, sizeof (tmp));
+ (void) strlcat(tmp, zpath, sizeof (tmp));
+ (void) strlcpy(zpath, tmp, sizeof (zpath));
+
+ (void) Plofspath(zpath, zpath, sizeof (zpath));
+ dprintf("Pzonepath found zone path (3) '%s'\n", zpath);
+
+ (void) strlcpy(s, zpath, n);
+ return (s);
+}
+
+char *
+Pfindobj(struct ps_prochandle *P, const char *path, char *s, size_t n)
+{
+ int len;
+
+ dprintf("Pfindobj '%s'\n", path);
+
+ /* We only deal with absolute paths */
+ if (path[0] != '/')
+ return (NULL);
+
+ /* First try to resolve the path to some zone */
+ if (Pzonepath(P, path, s, n) != NULL)
+ return (s);
+
+ /* If that fails resolve any lofs links in the path */
+ if (Plofspath(path, s, n) != NULL)
+ return (s);
+
+ /* If that fails then just see if the path exists */
+ if ((len = resolvepath(path, s, n)) > 0) {
+ s[len] = '\0';
+ return (s);
+ }
+
+ return (NULL);
+}
+
+char *
+Pfindmap(struct ps_prochandle *P, map_info_t *mptr, char *s, size_t n)
+{
+ file_info_t *fptr = mptr->map_file;
+ char buf[PATH_MAX];
+ int len;
+
+ /* If it's already been explicity set return that */
+ if ((fptr != NULL) && (fptr->file_rname != NULL)) {
+ (void) strlcpy(s, fptr->file_rname, n);
+ return (s);
+ }
+
+ /* If it's the a.out segment, defer to the magical Pexecname() */
+ if ((P->map_exec == mptr) ||
+ (strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) ||
+ ((fptr != NULL) && (fptr->file_lname != NULL) &&
+ (strcmp(fptr->file_lname, "a.out") == 0))) {
+ (void) Pexecname(P, buf, sizeof (buf));
+ (void) strlcpy(s, buf, n);
+ return (s);
+ }
+
+ /* Try /proc first to get the real object name */
+ if ((Pstate(P) != PS_DEAD) && (mptr->map_pmap.pr_mapname[0] != '\0')) {
+ (void) snprintf(buf, sizeof (buf), "%s/%d/path/%s",
+ procfs_path, (int)P->pid, mptr->map_pmap.pr_mapname);
+ if ((len = readlink(buf, buf, sizeof (buf))) > 0) {
+ buf[len] = '\0';
+ (void) Plofspath(buf, buf, sizeof (buf));
+ (void) strlcpy(s, buf, n);
+ return (s);
+ }
+ }
+
+ /*
+ * If we couldn't get the name from /proc, take the lname and
+ * try to expand it on the current system to a real object path.
+ */
+ fptr = mptr->map_file;
+ if ((fptr != NULL) && (fptr->file_lname != NULL)) {
+ (void) strlcpy(buf, fptr->file_lname, sizeof (buf));
+ if (Pfindobj(P, buf, buf, sizeof (buf)) == NULL)
+ return (NULL);
+ (void) strlcpy(s, buf, n);
+ return (s);
+ }
+
+ return (NULL);
+}