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 (void) strlcpy(tmp2, special, sizeof (tmp2) - 1);
392 (void) strlcat(tmp2, "/", sizeof (tmp2) - 1);
393 (void) strlcat(tmp2, &p[1], sizeof (tmp2) - 1);
394 (void) strlcpy(tmp, tmp2, sizeof (tmp) - 1);
395 if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
396 tmp[rv] = '\0';
397 p = &tmp[strlen(tmp)];
398 p[1] = '\0';
399 continue;
400 }
401
402 /* No lofs mount found */
403 if ((p2 = strrchr(tmp, '/')) == NULL) {
404 char tmp2[PATH_MAX];
405
406 (void) mutex_unlock(&lofs_lock);
407
408 /*
409 * We know that tmp was an absolute path, so if we
410 * made it here we know that (p == tmp) and that
411 * (*p == '\0'). This means that we've managed
412 * to check the whole path and so we're done.
413 */
414 assert(p == tmp);
415 assert(p[0] == '\0');
416
417 /* Restore the leading '/' in the path */
418 p[0] = '/';
419
420 if (strcmp(tmp, path) == 0) {
421 /* The path didn't change */
422 return (NULL);
423 }
424
425 /*
426 * It's possible that lofs source path we just
427 * obtained contains a symbolic link. Use
428 * resolvepath() to clean it up.
429 */
430 (void) strlcpy(tmp2, tmp, sizeof (tmp2));
431 if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
432 tmp[rv] = '\0';
433
434 /*
435 * It's always possible that our lofs source path is
436 * actually another lofs mount. So call ourselves
437 * recursively to resolve that path.
438 */
439 (void) Plofspath(tmp, tmp, PATH_MAX);
440
441 /* Copy out our final resolved lofs source path */
442 (void) strlcpy(s, tmp, n);
443 dprintf("Plofspath path result '%s'\n", s);
444 return (s);
445 }
446
447 /*
448 * So the path we just checked is not a lofs mount. Next we
449 * want to check the parent path component for a lofs mount.
450 *
451 * First, restore any '/' that we replaced with a '\0' (NUL).
452 * We can determine if we should do this by looking at p[1].
453 * If p[1] points to a '\0' (NUL) then we know that p points
454 * to the end of the string and there is no '/' to restore.
455 * if p[1] doesn't point to a '\0' (NUL) then it points to
456 * the part of the path that we've already verified so there
457 * is a '/' to restore.
458 */
459 if (p[1] != '\0')
460 p[0] = '/';
461
462 /*
463 * Second, replace the last '/' in the part of the path
464 * that we've already checked with a '\0' (NUL) so that
465 * when we loop around we check the parent component of the
466 * path.
467 */
468 p2[0] = '\0';
469 p = p2;
470 }
471 /*NOTREACHED*/
472 }
473
474 /*
475 * Pzonepath() - Way too much code to attempt to derive the full path of
476 * an object within a zone.
477 *
478 * Pzonepath() takes a path and attempts to resolve it relative to the
479 * root associated with the current process handle. If it fails it will
480 * not update the results string. It is safe to specify the same pointer
481 * for the file string and the results string.
482 *
483 * Doing this resolution is more difficult than it initially sounds.
484 * We can't simply append the file path to the zone root, because in
485 * a root directory, '..' is treated the same as '.'. Also, symbolic
486 * links that specify an absolute path need to be interpreted relative
487 * to the zone root.
488 *
489 * It seems like perhaps we could do a chroot(<zone root>) followed by a
490 * resolvepath(). But we can't do this because chroot requires special
491 * privileges and affects the entire process. Perhaps if there was a
492 * special version of resolvepath() which took an addition root path
493 * we could use that, but this isn't ideal either. The reason is
494 * that we want to have special handling for native paths. (A native path
495 * is a path that begins with "/native/" or "/.SUNWnative/".) Native
496 * paths could be passed explicity to this function or could be embedded
497 * in a symlink that is part of the path passed into this function.
498 * These paths are always lofs mounts of global zone paths, but lofs
499 * mounts only exist when a zone is booted. So if we were to try to do
500 * a resolvepath() on a native path when the zone wasn't booted the
501 * resolvepath() would fail even though we know that the components
502 * exists in the global zone.
503 *
504 * Given all these constraints, we just implement a path walking function
505 * that resolves a file path relative to a zone root by manually inspecting
506 * each of the path components and verifying its existence. This means that
507 * we must have access to the zone and that all the components of the
508 * path must exist for this operation to succeed.
509 */
510 char *
511 Pzonepath(struct ps_prochandle *P, const char *path, char *s, size_t n)
512 {
513 char zroot[PATH_MAX], zpath[PATH_MAX], tmp[PATH_MAX], link[PATH_MAX];
514 path_node_t *pn_stack = NULL, *pn_links = NULL, *pn;
515 struct stat64 sb;
516 char *p;
517 int i, rv;
518
519 dprintf("Pzonepath lookup '%s'\n", path);
520
521 /* First lookup the zone root */
522 if (Pzoneroot(P, zroot, sizeof (zroot)) == NULL)
523 return (NULL);
524
525 /*
526 * Make a temporary copy of the path specified.
527 * If it's a relative path then make it into an absolute path.
528 */
529 tmp[0] = '\0';
530 if (path[0] != '/')
531 (void) strlcat(tmp, "/", sizeof (tmp));
532 (void) strlcat(tmp, path, sizeof (tmp));
533
534 /*
535 * If the path that was passed in is the zone root, we're done.
536 * If the path that was passed in already contains the zone root
537 * then strip the zone root out and verify the rest of the path.
538 */
539 if (strcmp(tmp, zroot) == 0) {
540 (void) Plofspath(zroot, zroot, sizeof (zroot));
541 dprintf("Pzonepath found zone path (1) '%s'\n", zroot);
542 (void) strlcpy(s, zroot, n);
543 return (s);
544 }
545 i = strlen(zroot);
546 if ((strncmp(tmp, zroot, i) == 0) && (tmp[i] == '/'))
547 (void) memmove(tmp, tmp + i, strlen(tmp + i) + 1);
548
549 /* If no path is passed in, then it maps to the zone root */
550 if (strlen(tmp) == 0) {
551 (void) Plofspath(zroot, zroot, sizeof (zroot));
552 dprintf("Pzonepath found zone path (2) '%s'\n", zroot);
553 (void) strlcpy(s, zroot, n);
554 return (s);
555 }
556
557 /*
558 * Push each path component that we plan to verify onto a stack of
559 * path components, with parent components at the top of the stack.
560 * So for example, if we're going to verify the path /foo/bar/bang
561 * then our stack will look like:
562 * foo (top)
563 * bar
564 * bang (bottom)
565 */
566 while ((p = strrchr(tmp, '/')) != NULL) {
567 *p = '\0';
568 if (pn_push(&pn_stack, &p[1]) != NULL)
569 continue;
570 pn_free(&pn_stack);
571 return (NULL);
572 }
573
574 /* We're going to store the final zone relative path in zpath */
575 *zpath = '\0';
576
577 while (pn_pop(&pn_stack, tmp) != NULL) {
578 /*
579 * Drop zero length path components (which come from
580 * consecutive '/'s) and '.' path components.
581 */
582 if ((strlen(tmp) == 0) || (strcmp(tmp, ".") == 0))
583 continue;
584
585 /*
586 * Check the current path component for '..', if found
587 * drop any previous path component.
588 */
589 if (strcmp(tmp, "..") == 0) {
590 if ((p = strrchr(zpath, '/')) != NULL)
591 *p = '\0';
592 continue;
593 }
594
595 /* The path we want to verify now is zpath + / + tmp. */
596 (void) strlcat(zpath, "/", sizeof (zpath));
597 (void) strlcat(zpath, tmp, sizeof (zpath));
598
599 /*
600 * Check if this is a native object. A native object is an
601 * object from the global zone that is running in a branded
602 * zone. These objects are lofs mounted into a zone. So if a
603 * branded zone is not booted then lofs mounts won't be setup
604 * so we won't be able to find these objects. Luckily, we know
605 * that they exist in the global zone with the same path sans
606 * the initial native component, so we'll just strip out the
607 * native component here.
608 */
609 if ((strncmp(zpath, "/native", sizeof ("/native")) == 0) ||
610 (strncmp(zpath, "/.SUNWnative",
611 sizeof ("/.SUNWnative")) == 0)) {
612
613 /* Free any cached symlink paths */
614 pn_free(&pn_links);
615
616 /* Reconstruct the path from our path component stack */
617 *zpath = '\0';
618 while (pn_pop(&pn_stack, tmp) != NULL) {
619 (void) strlcat(zpath, "/", sizeof (zpath));
620 (void) strlcat(zpath, tmp, sizeof (zpath));
621 }
622
623 /* Verify that the path actually exists */
624 rv = resolvepath(zpath, tmp, sizeof (tmp) - 1);
625 if (rv < 0) {
626 dprintf("Pzonepath invalid native path '%s'\n",
627 zpath);
628 return (NULL);
629 }
630 tmp[rv] = '\0';
631
632 /* Return the path */
633 dprintf("Pzonepath found native path '%s'\n", tmp);
634 (void) Plofspath(tmp, tmp, sizeof (tmp));
635 (void) strlcpy(s, tmp, n);
636 return (s);
637 }
638
639 /*
640 * Check if the path points to a symlink. We do this
641 * explicitly since any absolute symlink needs to be
642 * interpreted relativly to the zone root and not "/".
643 */
644 (void) strlcpy(tmp, zroot, sizeof (tmp));
645 (void) strlcat(tmp, zpath, sizeof (tmp));
646 if (lstat64(tmp, &sb) != 0) {
647 pn_free2(&pn_stack, &pn_links);
648 return (NULL);
649 }
650 if (!S_ISLNK(sb.st_mode)) {
651 /*
652 * Since the lstat64() above succeeded we know that
653 * zpath exists, since this is not a symlink loop
654 * around and check the next path component.
655 */
656 continue;
657 }
658
659 /*
660 * Symlink allow for paths with loops. Make sure
661 * we're not stuck in a loop.
662 */
663 for (pn = pn_links; pn != NULL; pn = pn->pn_next) {
664 if (strcmp(zpath, pn->pn_path) != 0)
665 continue;
666
667 /* We have a loop. Fail. */
668 dprintf("Pzonepath symlink loop '%s'\n", zpath);
669 pn_free2(&pn_stack, &pn_links);
670 return (NULL);
671 }
672
673 /* Save this symlink path for future loop checks */
674 if (pn_push(&pn_links, zpath) == NULL) {
675 /* Out of memory */
676 pn_free2(&pn_stack, &pn_links);
677 return (NULL);
678 }
679
680 /* Now follow the contents of the symlink */
681 bzero(link, sizeof (link));
682 if (readlink(tmp, link, sizeof (link)) == -1) {
683 pn_free2(&pn_stack, &pn_links);
684 return (NULL);
685 }
686
687 dprintf("Pzonepath following symlink '%s' -> '%s'\n",
688 zpath, link);
689
690 /*
691 * Push each path component of the symlink target onto our
692 * path components stack since we need to verify each one.
693 */
694 while ((p = strrchr(link, '/')) != NULL) {
695 *p = '\0';
696 if (pn_push(&pn_stack, &p[1]) != NULL)
697 continue;
698 pn_free2(&pn_stack, &pn_links);
699 return (NULL);
700 }
701
702 /* absolute or relative symlink? */
703 if (*link == '\0') {
704 /* Absolute symlink, nuke existing zpath. */
705 *zpath = '\0';
706 continue;
707 }
708
709 /*
710 * Relative symlink. Push the first path component of the
711 * symlink target onto our stack for verification and then
712 * remove the current path component from zpath.
713 */
714 if (pn_push(&pn_stack, link) == NULL) {
715 pn_free2(&pn_stack, &pn_links);
716 return (NULL);
717 }
718 p = strrchr(zpath, '/');
719 assert(p != NULL);
720 *p = '\0';
721 continue;
722 }
723 pn_free(&pn_links);
724
725 /* Place the final result in zpath */
726 (void) strlcpy(tmp, zroot, sizeof (tmp));
727 (void) strlcat(tmp, zpath, sizeof (tmp));
728 (void) strlcpy(zpath, tmp, sizeof (zpath));
729
730 (void) Plofspath(zpath, zpath, sizeof (zpath));
731 dprintf("Pzonepath found zone path (3) '%s'\n", zpath);
732
733 (void) strlcpy(s, zpath, n);
734 return (s);
735 }
736
737 char *
738 Pfindobj(struct ps_prochandle *P, const char *path, char *s, size_t n)
739 {
740 int len;
741
742 dprintf("Pfindobj '%s'\n", path);
743
744 /* We only deal with absolute paths */
745 if (path[0] != '/')
746 return (NULL);
747
748 /* First try to resolve the path to some zone */
749 if (Pzonepath(P, path, s, n) != NULL)
750 return (s);
751
752 /* If that fails resolve any lofs links in the path */
753 if (Plofspath(path, s, n) != NULL)
754 return (s);
755
756 /* If that fails then just see if the path exists */
757 if ((len = resolvepath(path, s, n)) > 0) {
758 s[len] = '\0';
759 return (s);
760 }
761
762 return (NULL);
763 }
764
765 char *
766 Pfindmap(struct ps_prochandle *P, map_info_t *mptr, char *s, size_t n)
767 {
768 file_info_t *fptr = mptr->map_file;
769 char buf[PATH_MAX];
770 int len;
771
772 /* If it's already been explicity set return that */
773 if ((fptr != NULL) && (fptr->file_rname != NULL)) {
774 (void) strlcpy(s, fptr->file_rname, n);
775 return (s);
776 }
777
778 /* If it's the a.out segment, defer to the magical Pexecname() */
779 if ((P->map_exec == mptr) ||
780 (strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) ||
781 ((fptr != NULL) && (fptr->file_lname != NULL) &&
782 (strcmp(fptr->file_lname, "a.out") == 0))) {
783 if (Pexecname(P, buf, sizeof (buf)) != NULL) {
784 (void) strlcpy(s, buf, n);
785 return (s);
786 }
787 }
788
789 /* Try /proc first to get the real object name */
790 if ((Pstate(P) != PS_DEAD) && (mptr->map_pmap.pr_mapname[0] != '\0')) {
791 (void) snprintf(buf, sizeof (buf), "%s/%d/path/%s",
792 procfs_path, (int)P->pid, mptr->map_pmap.pr_mapname);
793 if ((len = readlink(buf, buf, sizeof (buf))) > 0) {
794 buf[len] = '\0';
795 (void) Plofspath(buf, buf, sizeof (buf));
796 (void) strlcpy(s, buf, n);
797 return (s);
798 }
799 }
800
801 /*
802 * If we couldn't get the name from /proc, take the lname and
803 * try to expand it on the current system to a real object path.
804 */
805 fptr = mptr->map_file;
806 if ((fptr != NULL) && (fptr->file_lname != NULL)) {
807 (void) strlcpy(buf, fptr->file_lname, sizeof (buf));
808 if (Pfindobj(P, buf, buf, sizeof (buf)) == NULL)
809 return (NULL);
810 (void) strlcpy(s, buf, n);
811 return (s);
812 }
813
814 return (NULL);
815 }