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