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