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 }