xref: /illumos-gate/usr/src/lib/libproc/common/Pzone.c (revision c9eab9d4e096bb9b983e9b007577edfa73c32eff)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <assert.h>
28 #include <dlfcn.h>
29 #include <errno.h>
30 #include <libzonecfg.h>
31 #include <link.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <sys/list.h>
35 #include <sys/types.h>
36 #include <sys/mkdev.h>
37 #include <sys/mman.h>
38 #include <sys/mnttab.h>
39 
40 #include "Pcontrol.h"
41 
42 struct path_node {
43 	struct path_node	*pn_next;
44 	char			*pn_path;
45 };
46 typedef struct path_node path_node_t;
47 
48 static path_node_t *
49 pn_push(path_node_t **pnp, char *path)
50 {
51 	path_node_t *pn;
52 
53 	if ((pn = calloc(sizeof (path_node_t), 1)) == NULL)
54 		return (NULL);
55 
56 	if ((pn->pn_path = strdup(path)) == NULL) {
57 		free(pn);
58 		return (NULL);
59 	}
60 	pn->pn_next = *pnp;
61 	return (*pnp = pn);
62 }
63 
64 static void
65 pn_free(path_node_t **pnp)
66 {
67 	path_node_t *pn;
68 
69 	while (*pnp != NULL) {
70 		pn = *pnp;
71 		*pnp = pn->pn_next;
72 		free(pn->pn_path);
73 		free(pn);
74 	}
75 }
76 
77 static void
78 pn_free2(path_node_t **pn1, path_node_t **pn2)
79 {
80 	pn_free(pn1);
81 	pn_free(pn2);
82 }
83 
84 static char *
85 pn_pop(path_node_t **pnp, char *path)
86 {
87 	path_node_t *pn;
88 
89 	if (*pnp == NULL)
90 		return (NULL);
91 
92 	pn = *pnp;
93 	*pnp = pn->pn_next;
94 	pn->pn_next = NULL;
95 
96 	if (path == NULL) {
97 		pn_free(&pn);
98 		return (NULL);
99 	}
100 	(void) strlcpy(path, pn->pn_path, PATH_MAX);
101 	pn_free(&pn);
102 	return (path);
103 }
104 
105 
106 /*
107  * Libzonecfg.so links against libproc, so libproc can't link against
108  * libzonecfg.so.  Also, libzonecfg.so is optional and might not be
109  * installed.  Hence instead of relying on linking to access libzonecfg.so,
110  * we'll try dlopening it here.  This trick is borrowed from
111  * libc`zone_get_id(), see that function for more detailed comments.
112  */
113 static int
114 i_zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
115 {
116 	typedef	int (*zone_get_zonepath_t)(char *, char *, size_t);
117 	static zone_get_zonepath_t zone_get_zonepath_fp = NULL;
118 
119 	if (zone_get_zonepath_fp == NULL) {
120 		/* There's no harm in doing this multiple times. */
121 		void *dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY);
122 		void *sym = (void *)(-1);
123 		if (dlhandle != NULL &&
124 		    (sym = dlsym(dlhandle, "zone_get_zonepath")) == NULL) {
125 			sym = (void *)(-1);
126 			(void) dlclose(dlhandle);
127 		}
128 		zone_get_zonepath_fp = (zone_get_zonepath_t)sym;
129 	}
130 
131 	/* If we've successfully loaded it, call the real function */
132 	if (zone_get_zonepath_fp != (zone_get_zonepath_t)(-1))
133 		return (zone_get_zonepath_fp(zone_name, zonepath, rp_sz));
134 	return (Z_NO_ZONE);
135 }
136 
137 char *
138 Pbrandname(struct ps_prochandle *P, char *buf, size_t buflen)
139 {
140 	long	addr;
141 
142 	if ((addr = Pgetauxval(P, AT_SUN_BRANDNAME)) == -1)
143 		return (NULL);
144 
145 	if (Pread_string(P, buf, buflen, addr) == -1)
146 		return (NULL);
147 
148 	return (buf);
149 }
150 
151 /*
152  * Get the zone name from the core file if we have it; look up the
153  * name based on the zone id if this is a live process.
154  */
155 char *
156 Pzonename(struct ps_prochandle *P, char *s, size_t n)
157 {
158 	if (P->state == PS_IDLE) {
159 		errno = ENODATA;
160 		return (NULL);
161 	}
162 
163 	if (P->state == PS_DEAD) {
164 		if (P->core->core_zonename == NULL) {
165 			errno = ENODATA;
166 			return (NULL);
167 		}
168 		(void) strlcpy(s, P->core->core_zonename, n);
169 	} else {
170 		if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0)
171 			return (NULL);
172 		s[n - 1] = '\0';
173 	}
174 	return (s);
175 }
176 
177 char *
178 Pzoneroot(struct ps_prochandle *P, char *s, size_t n)
179 {
180 	char zname[ZONENAME_MAX], zpath[PATH_MAX], tmp[PATH_MAX];
181 	int rv;
182 
183 	if (P->zoneroot != NULL) {
184 		(void) strlcpy(s, P->zoneroot, n);
185 		return (s);
186 	}
187 
188 	if ((Pzonename(P, zname, sizeof (zname)) == NULL) ||
189 	    (strcmp(zname, GLOBAL_ZONENAME) == 0)) {
190 		if ((P->zoneroot = strdup("")) == NULL) {
191 			errno = ENOMEM;
192 			return (NULL);
193 		}
194 		dprintf("Pzoneroot defaulting to '%s'\n", GLOBAL_ZONENAME);
195 		(void) strlcpy(s, P->zoneroot, n);
196 		return (s);
197 	}
198 
199 	if (i_zone_get_zonepath(zname, zpath, sizeof (zpath)) != Z_OK) {
200 		if ((P->zoneroot = strdup("")) == NULL) {
201 			errno = ENOMEM;
202 			return (NULL);
203 		}
204 		dprintf(
205 		    "Pzoneroot zone not found '%s', defaulting to '%s'\n",
206 		    zname, GLOBAL_ZONENAME);
207 		(void) strlcpy(s, P->zoneroot, n);
208 		return (s);
209 	}
210 	(void) strlcat(zpath, "/root", sizeof (zpath));
211 
212 	if ((rv = resolvepath(zpath, tmp, sizeof (tmp) - 1)) < 0) {
213 		if ((P->zoneroot = strdup("")) == NULL) {
214 			errno = ENOMEM;
215 			return (NULL);
216 		}
217 		dprintf(
218 		    "Pzoneroot can't access '%s:%s', defaulting to '%s'\n",
219 		    zname, zpath, GLOBAL_ZONENAME);
220 		(void) strlcpy(s, P->zoneroot, n);
221 		return (s);
222 	}
223 	tmp[rv] = '\0';
224 	(void) strlcpy(zpath, tmp, sizeof (zpath));
225 
226 	if ((P->zoneroot = strdup(zpath)) == NULL) {
227 		errno = ENOMEM;
228 		return (NULL);
229 	}
230 	dprintf("Pzoneroot found zone root '%s:%s'\n", zname, zpath);
231 	(void) strlcpy(s, P->zoneroot, n);
232 	return (s);
233 }
234 
235 /*
236  * Plofspath() takes a path, "path",  and removes any lofs components from
237  * that path.  The resultant path (if different from the starting path)
238  * is placed in "s", which is limited to "n" characters, and the return
239  * value is the pointer s.  If there are no lofs components in the path
240  * the NULL is returned and s is not modified.  It's ok for "path" and
241  * "s" to be the same pointer.  (ie, the results can be stored directly
242  * in the input buffer.)  The path that is passed in must be an absolute
243  * path.
244  *
245  * Example:
246  *	if "path" == "/foo/bar", and "/candy/" is lofs mounted on "/foo/"
247  *	then "/candy/bar/" will be written into "s" and "s" will be returned.
248  */
249 char *
250 Plofspath(const char *path, char *s, size_t n)
251 {
252 	char tmp[PATH_MAX + 1];
253 	struct mnttab mt, mt_find;
254 	FILE *fp;
255 	char *p, *p2;
256 	int rv;
257 
258 	dprintf("Plofspath path '%s'\n", path);
259 
260 	/* We only deal with absolute paths */
261 	if (path[0] != '/')
262 		return (NULL);
263 
264 	/* Open /etc/mnttab */
265 	if ((fp = fopen(MNTTAB, "r")) == NULL)
266 		return (NULL);
267 
268 	/* Make a copy of the path so that we can muck with it */
269 	(void) strlcpy(tmp, path, sizeof (tmp) - 1);
270 
271 	/*
272 	 * Use resolvepath() to make sure there are no consecutive or
273 	 * trailing '/'s in the path.
274 	 */
275 	if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
276 		tmp[rv] = '\0';
277 
278 	/*
279 	 * So now we're going to search the path for any components that
280 	 * might be lofs mounts.  We'll start out search from the full
281 	 * path and then step back through each parent directly till
282 	 * we reach the root.  If we find a lofs mount point in the path
283 	 * then we'll replace the initial portion of the path (up
284 	 * to that mount point) with the source of that mount point
285 	 * and then start our search over again.
286 	 *
287 	 * Here's some of the variables we're going to use:
288 	 *
289 	 *	tmp - A pointer to our working copy of the path.  Sometimes
290 	 *		this path will be divided into two strings by a
291 	 *		'\0' (NUL) character.  The first string is the
292 	 *		component we're currently checking and the second
293 	 *		string is the path components we've already checked.
294 	 *
295 	 *	p - A pointer to the last '/' seen in the string.
296 	 *
297 	 *	p[1] - A pointer to the component of the string we've already
298 	 *		checked.
299 	 *
300 	 * Initially, p will point to the end of our path and p[1] will point
301 	 * to an extra '\0' (NUL) that we'll append to the end of the string.
302 	 * (This is why we declared tmp with a size of PATH_MAX + 1).
303 	 */
304 	p = &tmp[strlen(tmp)];
305 	p[1] = '\0';
306 	for (;;) {
307 		/* Check if tmp is a mount point */
308 		rewind(fp);
309 		bzero(&mt_find, sizeof (mt_find));
310 		mt_find.mnt_mountp = tmp;
311 		rv = getmntany(fp, &mt, &mt_find);
312 
313 		/*
314 		 * We only care about lofs mount points.  But we need to
315 		 * ignore lofs mounts where the source path is the same
316 		 * as the target path.  (This can happen when a non-global
317 		 * zone has a lofs mount of a global zone filesystem, since
318 		 * the source path can't expose information about global
319 		 * zone paths to the non-global zone.)
320 		 */
321 		if ((rv == 0) && (strcmp(mt.mnt_fstype, "lofs") == 0) &&
322 		    (strcmp(mt.mnt_special, mt.mnt_mountp) != 0)) {
323 			char tmp2[PATH_MAX + 1];
324 
325 			/*
326 			 * We found a lofs mount.  Update the path that we're
327 			 * checking and start over.  This means append the
328 			 * portion of the path we've already checked to the
329 			 * source of the lofs mount and re-start this entire
330 			 * lofs resolution loop.  Use resolvepath() to make
331 			 * sure there are no consecutive or trailing '/'s
332 			 * in the path.
333 			 */
334 			(void) strlcpy(tmp2, mt.mnt_special, sizeof (tmp2) - 1);
335 			(void) strlcat(tmp2, "/", sizeof (tmp2) - 1);
336 			(void) strlcat(tmp2, &p[1], sizeof (tmp2) - 1);
337 			(void) strlcpy(tmp, tmp2, sizeof (tmp) - 1);
338 			if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
339 				tmp[rv] = '\0';
340 			p = &tmp[strlen(tmp)];
341 			p[1] = '\0';
342 			continue;
343 		}
344 
345 		/* No lofs mount found */
346 		if ((p2 = strrchr(tmp, '/')) == NULL) {
347 			char tmp2[PATH_MAX];
348 
349 			/*
350 			 * We know that tmp was an absolute path, so if we
351 			 * made it here we know that (p == tmp) and that
352 			 * (*p == '\0').  This means that we've managed
353 			 * to check the whole path and so we're done.
354 			 */
355 			assert(p == tmp);
356 			assert(p[0] == '\0');
357 			(void) fclose(fp);
358 
359 			/* Restore the leading '/' in the path */
360 			p[0] = '/';
361 
362 			if (strcmp(tmp, path) == 0) {
363 				/* The path didn't change */
364 				return (NULL);
365 			}
366 
367 			/*
368 			 * It's possible that lofs source path we just
369 			 * obtained contains a symbolic link.  Use
370 			 * resolvepath() to clean it up.
371 			 */
372 			(void) strlcpy(tmp2, tmp, sizeof (tmp2));
373 			if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
374 				tmp[rv] = '\0';
375 
376 			/*
377 			 * It's always possible that our lofs source path is
378 			 * actually another lofs mount.  So call ourselves
379 			 * recursively to resolve that path.
380 			 */
381 			(void) Plofspath(tmp, tmp, PATH_MAX);
382 
383 			/* Copy out our final resolved lofs source path */
384 			(void) strlcpy(s, tmp, n);
385 			dprintf("Plofspath path result '%s'\n", s);
386 			return (s);
387 		}
388 
389 		/*
390 		 * So the path we just checked is not a lofs mount.  Next we
391 		 * want to check the parent path component for a lofs mount.
392 		 *
393 		 * First, restore any '/' that we replaced with a '\0' (NUL).
394 		 * We can determine if we should do this by looking at p[1].
395 		 * If p[1] points to a '\0' (NUL) then we know that p points
396 		 * to the end of the string and there is no '/' to restore.
397 		 * if p[1] doesn't point to a '\0' (NUL) then it points to
398 		 * the part of the path that we've already verified so there
399 		 * is a '/' to restore.
400 		 */
401 		if (p[1] != '\0')
402 			p[0] = '/';
403 
404 		/*
405 		 * Second, replace the last '/' in the part of the path
406 		 * that we've already checked with a '\0' (NUL) so that
407 		 * when we loop around we check the parent component of the
408 		 * path.
409 		 */
410 		p2[0] = '\0';
411 		p = p2;
412 	}
413 	/*NOTREACHED*/
414 }
415 
416 /*
417  * Pzonepath() - Way too much code to attempt to derive the full path of
418  * an object within a zone.
419  *
420  * Pzonepath() takes a path and attempts to resolve it relative to the
421  * root associated with the current process handle.  If it fails it will
422  * not update the results string.  It is safe to specify the same pointer
423  * for the file string and the results string.
424  *
425  * Doing this resolution is more difficult than it initially sounds.
426  * We can't simply append the file path to the zone root, because in
427  * a root directory, '..' is treated the same as '.'.  Also, symbolic
428  * links that specify an absolute path need to be interpreted relative
429  * to the zone root.
430  *
431  * It seems like perhaps we could do a chroot(<zone root>) followed by a
432  * resolvepath().  But we can't do this because chroot requires special
433  * privileges and affects the entire process.  Perhaps if there was a
434  * special version of resolvepath() which took an addition root path
435  * we could use that, but this isn't ideal either.  The reason is
436  * that we want to have special handling for native paths.  (A native path
437  * is a path that begins with "/native/" or "/.SUNWnative/".)  Native
438  * paths could be passed explicity to this function or could be embedded
439  * in a symlink that is part of the path passed into this function.
440  * These paths are always lofs mounts of global zone paths, but lofs
441  * mounts only exist when a zone is booted.  So if we were to try to do
442  * a resolvepath() on a native path when the zone wasn't booted the
443  * resolvepath() would fail even though we know that the components
444  * exists in the global zone.
445  *
446  * Given all these constraints, we just implement a path walking function
447  * that resolves a file path relative to a zone root by manually inspecting
448  * each of the path components and verifying its existence.  This means that
449  * we must have access to the zone and that all the components of the
450  * path must exist for this operation to succeed.
451  */
452 char *
453 Pzonepath(struct ps_prochandle *P, const char *path, char *s, size_t n)
454 {
455 	char zroot[PATH_MAX], zpath[PATH_MAX], tmp[PATH_MAX], link[PATH_MAX];
456 	path_node_t *pn_stack = NULL, *pn_links = NULL, *pn;
457 	struct stat64 sb;
458 	char *p;
459 	int i, rv;
460 
461 	dprintf("Pzonepath lookup '%s'\n", path);
462 
463 	/* First lookup the zone root */
464 	if (Pzoneroot(P, zroot, sizeof (zroot)) == NULL)
465 		return (NULL);
466 
467 	/*
468 	 * Make a temporary copy of the path specified.
469 	 * If it's a relative path then make it into an absolute path.
470 	 */
471 	tmp[0] = '\0';
472 	if (path[0] != '/')
473 		(void) strlcat(tmp, "/", sizeof (tmp));
474 	(void) strlcat(tmp, path, sizeof (tmp));
475 
476 	/*
477 	 * If the path that was passed in is the zone root, we're done.
478 	 * If the path that was passed in already contains the zone root
479 	 * then strip the zone root out and verify the rest of the path.
480 	 */
481 	if (strcmp(tmp, zroot) == 0) {
482 		(void) Plofspath(zroot, zroot, sizeof (zroot));
483 		dprintf("Pzonepath found zone path (1) '%s'\n", zroot);
484 		(void) strlcpy(s, zroot, n);
485 		return (s);
486 	}
487 	i = strlen(zroot);
488 	if ((strncmp(tmp, zroot, i) == 0) && (tmp[i] == '/'))
489 		(void) memmove(tmp, tmp + i, strlen(tmp + i) + 1);
490 
491 	/* If no path is passed in, then it maps to the zone root */
492 	if (strlen(tmp) == 0) {
493 		(void) Plofspath(zroot, zroot, sizeof (zroot));
494 		dprintf("Pzonepath found zone path (2) '%s'\n", zroot);
495 		(void) strlcpy(s, zroot, n);
496 		return (s);
497 	}
498 
499 	/*
500 	 * Push each path component that we plan to verify onto a stack of
501 	 * path components, with parent components at the top of the stack.
502 	 * So for example, if we're going to verify the path /foo/bar/bang
503 	 * then our stack will look like:
504 	 *	foo	(top)
505 	 *	bar
506 	 *	bang	(bottom)
507 	 */
508 	while ((p = strrchr(tmp, '/')) != NULL) {
509 		*p = '\0';
510 		if (pn_push(&pn_stack, &p[1]) != NULL)
511 			continue;
512 		pn_free(&pn_stack);
513 		return (NULL);
514 	}
515 
516 	/* We're going to store the final zone relative path in zpath */
517 	*zpath = '\0';
518 
519 	while (pn_pop(&pn_stack, tmp) != NULL) {
520 		/*
521 		 * Drop zero length path components (which come from
522 		 * consecutive '/'s) and '.' path components.
523 		 */
524 		if ((strlen(tmp) == 0) || (strcmp(tmp, ".") == 0))
525 			continue;
526 
527 		/*
528 		 * Check the current path component for '..', if found
529 		 * drop any previous path component.
530 		 */
531 		if (strcmp(tmp, "..") == 0) {
532 			if ((p = strrchr(zpath, '/')) != NULL)
533 				*p = '\0';
534 			continue;
535 		}
536 
537 		/* The path we want to verify now is zpath + / + tmp. */
538 		(void) strlcat(zpath, "/", sizeof (zpath));
539 		(void) strlcat(zpath, tmp, sizeof (zpath));
540 
541 		/*
542 		 * Check if this is a native object.  A native object is an
543 		 * object from the global zone that is running in a branded
544 		 * zone.  These objects are lofs mounted into a zone.  So if a
545 		 * branded zone is not booted then lofs mounts won't be setup
546 		 * so we won't be able to find these objects.  Luckily, we know
547 		 * that they exist in the global zone with the same path sans
548 		 * the initial native component, so we'll just strip out the
549 		 * native component here.
550 		 */
551 		if ((strncmp(zpath, "/native", sizeof ("/native")) == 0) ||
552 		    (strncmp(zpath, "/.SUNWnative",
553 		    sizeof ("/.SUNWnative")) == 0)) {
554 
555 			/* Free any cached symlink paths */
556 			pn_free(&pn_links);
557 
558 			/* Reconstruct the path from our path component stack */
559 			*zpath = '\0';
560 			while (pn_pop(&pn_stack, tmp) != NULL) {
561 				(void) strlcat(zpath, "/", sizeof (zpath));
562 				(void) strlcat(zpath, tmp, sizeof (zpath));
563 			}
564 
565 			/* Verify that the path actually exists */
566 			rv = resolvepath(zpath, tmp, sizeof (tmp) - 1);
567 			if (rv < 0) {
568 				dprintf("Pzonepath invalid native path '%s'\n",
569 				    zpath);
570 				return (NULL);
571 			}
572 			tmp[rv] = '\0';
573 
574 			/* Return the path */
575 			dprintf("Pzonepath found native path '%s'\n", tmp);
576 			(void) Plofspath(tmp, tmp, sizeof (tmp));
577 			(void) strlcpy(s, tmp, n);
578 			return (s);
579 		}
580 
581 		/*
582 		 * Check if the path points to a symlink.  We do this
583 		 * explicitly since any absolute symlink needs to be
584 		 * interpreted relativly to the zone root and not "/".
585 		 */
586 		(void) strlcpy(tmp, zroot, sizeof (tmp));
587 		(void) strlcat(tmp, zpath, sizeof (tmp));
588 		if (lstat64(tmp, &sb) != 0) {
589 			pn_free2(&pn_stack, &pn_links);
590 			return (NULL);
591 		}
592 		if (!S_ISLNK(sb.st_mode)) {
593 			/*
594 			 * Since the lstat64() above succeeded we know that
595 			 * zpath exists, since this is not a symlink loop
596 			 * around and check the next path component.
597 			 */
598 			continue;
599 		}
600 
601 		/*
602 		 * Symlink allow for paths with loops.  Make sure
603 		 * we're not stuck in a loop.
604 		 */
605 		for (pn = pn_links; pn != NULL; pn = pn->pn_next) {
606 			if (strcmp(zpath, pn->pn_path) != 0)
607 				continue;
608 
609 			/* We have a loop.  Fail. */
610 			dprintf("Pzonepath symlink loop '%s'\n", zpath);
611 			pn_free2(&pn_stack, &pn_links);
612 			return (NULL);
613 		}
614 
615 		/* Save this symlink path for future loop checks */
616 		if (pn_push(&pn_links, zpath) == NULL) {
617 			/* Out of memory */
618 			pn_free2(&pn_stack, &pn_links);
619 			return (NULL);
620 		}
621 
622 		/* Now follow the contents of the symlink */
623 		bzero(link, sizeof (link));
624 		if (readlink(tmp, link, sizeof (link)) == -1) {
625 			pn_free2(&pn_stack, &pn_links);
626 			return (NULL);
627 		}
628 
629 		dprintf("Pzonepath following symlink '%s' -> '%s'\n",
630 		    zpath, link);
631 
632 		/*
633 		 * Push each path component of the symlink target onto our
634 		 * path components stack since we need to verify each one.
635 		 */
636 		while ((p = strrchr(link, '/')) != NULL) {
637 			*p = '\0';
638 			if (pn_push(&pn_stack, &p[1]) != NULL)
639 				continue;
640 			pn_free2(&pn_stack, &pn_links);
641 			return (NULL);
642 		}
643 
644 		/* absolute or relative symlink? */
645 		if (*link == '\0') {
646 			/* Absolute symlink, nuke existing zpath. */
647 			*zpath = '\0';
648 			continue;
649 		}
650 
651 		/*
652 		 * Relative symlink.  Push the first path component of the
653 		 * symlink target onto our stack for verification and then
654 		 * remove the current path component from zpath.
655 		 */
656 		if (pn_push(&pn_stack, link) == NULL) {
657 			pn_free2(&pn_stack, &pn_links);
658 			return (NULL);
659 		}
660 		p = strrchr(zpath, '/');
661 		assert(p != NULL);
662 		*p = '\0';
663 		continue;
664 	}
665 	pn_free(&pn_links);
666 
667 	/* Place the final result in zpath */
668 	(void) strlcpy(tmp, zroot, sizeof (tmp));
669 	(void) strlcat(tmp, zpath, sizeof (tmp));
670 	(void) strlcpy(zpath, tmp, sizeof (zpath));
671 
672 	(void) Plofspath(zpath, zpath, sizeof (zpath));
673 	dprintf("Pzonepath found zone path (3) '%s'\n", zpath);
674 
675 	(void) strlcpy(s, zpath, n);
676 	return (s);
677 }
678 
679 char *
680 Pfindobj(struct ps_prochandle *P, const char *path, char *s, size_t n)
681 {
682 	int len;
683 
684 	dprintf("Pfindobj '%s'\n", path);
685 
686 	/* We only deal with absolute paths */
687 	if (path[0] != '/')
688 		return (NULL);
689 
690 	/* First try to resolve the path to some zone */
691 	if (Pzonepath(P, path, s, n) != NULL)
692 		return (s);
693 
694 	/* If that fails resolve any lofs links in the path */
695 	if (Plofspath(path, s, n) != NULL)
696 		return (s);
697 
698 	/* If that fails then just see if the path exists */
699 	if ((len = resolvepath(path, s, n)) > 0) {
700 		s[len] = '\0';
701 		return (s);
702 	}
703 
704 	return (NULL);
705 }
706 
707 char *
708 Pfindmap(struct ps_prochandle *P, map_info_t *mptr, char *s, size_t n)
709 {
710 	file_info_t *fptr = mptr->map_file;
711 	char buf[PATH_MAX];
712 	int len;
713 
714 	/* If it's already been explicity set return that */
715 	if ((fptr != NULL) && (fptr->file_rname != NULL)) {
716 		(void) strlcpy(s, fptr->file_rname, n);
717 		return (s);
718 	}
719 
720 	/* If it's the a.out segment, defer to the magical Pexecname() */
721 	if ((P->map_exec == mptr) ||
722 	    (strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) ||
723 	    ((fptr != NULL) && (fptr->file_lname != NULL) &&
724 	    (strcmp(fptr->file_lname, "a.out") == 0))) {
725 		(void) Pexecname(P, buf, sizeof (buf));
726 		(void) strlcpy(s, buf, n);
727 		return (s);
728 	}
729 
730 	/* Try /proc first to get the real object name */
731 	if ((Pstate(P) != PS_DEAD) && (mptr->map_pmap.pr_mapname[0] != '\0')) {
732 		(void) snprintf(buf, sizeof (buf), "%s/%d/path/%s",
733 		    procfs_path, (int)P->pid, mptr->map_pmap.pr_mapname);
734 		if ((len = readlink(buf, buf, sizeof (buf))) > 0) {
735 			buf[len] = '\0';
736 			(void) Plofspath(buf, buf, sizeof (buf));
737 			(void) strlcpy(s, buf, n);
738 			return (s);
739 		}
740 	}
741 
742 	/*
743 	 * If we couldn't get the name from /proc, take the lname and
744 	 * try to expand it on the current system to a real object path.
745 	 */
746 	fptr = mptr->map_file;
747 	if ((fptr != NULL) && (fptr->file_lname != NULL)) {
748 		(void) strlcpy(buf, fptr->file_lname, sizeof (buf));
749 		if (Pfindobj(P, buf, buf, sizeof (buf)) == NULL)
750 			return (NULL);
751 		(void) strlcpy(s, buf, n);
752 		return (s);
753 	}
754 
755 	return (NULL);
756 }
757