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
42 struct path_node {
43 struct path_node *pn_next;
44 char *pn_path;
45 };
46 typedef struct path_node path_node_t;
47
48 /*
49 * Parameters of the lofs lookup cache.
50 */
51 static struct stat64 lofs_mstat; /* last stat() of MNTTAB */
52 static 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;
57 static mutex_t lofs_lock = DEFAULTMUTEX; /* protects the lofs cache */
58
59 static void
rebuild_lofs_cache(void)60 rebuild_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
107 static const char *
lookup_lofs_mount_point(const char * mountp)108 lookup_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
121 static path_node_t *
pn_push(path_node_t ** pnp,char * path)122 pn_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
137 static void
pn_free(path_node_t ** pnp)138 pn_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
150 static void
pn_free2(path_node_t ** pn1,path_node_t ** pn2)151 pn_free2(path_node_t **pn1, path_node_t **pn2)
152 {
153 pn_free(pn1);
154 pn_free(pn2);
155 }
156
157 static char *
pn_pop(path_node_t ** pnp,char * path)158 pn_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 */
186 static int
i_zone_get_zonepath(char * zone_name,char * zonepath,size_t rp_sz)187 i_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
210 char *
Pbrandname(struct ps_prochandle * P,char * buf,size_t buflen)211 Pbrandname(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 */
228 char *
Pzonename(struct ps_prochandle * P,char * s,size_t n)229 Pzonename(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
250 char *
Pzoneroot(struct ps_prochandle * P,char * s,size_t n)251 Pzoneroot(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 */
322 char *
Plofspath(const char * path,char * s,size_t n)323 Plofspath(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 */
522 char *
Pzonepath(struct ps_prochandle * P,const char * path,char * s,size_t n)523 Pzonepath(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
749 char *
Pfindobj(struct ps_prochandle * P,const char * path,char * s,size_t n)750 Pfindobj(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
777 char *
Pfindmap(struct ps_prochandle * P,map_info_t * mptr,char * s,size_t n)778 Pfindmap(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