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