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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
28 * Copyright (c) 2013 by Delphix. All rights reserved.
34 #include <libzonecfg.h>
39 #include <sys/types.h>
40 #include <sys/mkdev.h>
42 #include <sys/mnttab.h>
47 struct path_node
*pn_next
;
50 typedef struct path_node path_node_t
;
53 * Parameters of the lofs lookup cache.
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 */
64 rebuild_lofs_cache(void)
67 struct mnttab mt_find
;
68 struct lofs_mnttab
*lmt
;
69 struct lofs_mnttab
*next
;
72 assert(MUTEX_HELD(&lofs_lock
));
74 /* destroy the old cache */
75 for (lmt
= lofs_mnttab
; lmt
!= NULL
; lmt
= next
) {
83 /* prepare to create the new cache */
84 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
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.)
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
)
102 lmt
->l_special
= strdup(mt
.mnt_special
);
103 lmt
->l_mountp
= strdup(mt
.mnt_mountp
);
104 lmt
->l_next
= lofs_mnttab
;
112 lookup_lofs_mount_point(const char *mountp
)
114 struct lofs_mnttab
*lmt
;
116 assert(MUTEX_HELD(&lofs_lock
));
118 for (lmt
= lofs_mnttab
; lmt
!= NULL
; lmt
= lmt
->l_next
) {
119 if (strcmp(lmt
->l_mountp
, mountp
) == 0)
120 return (lmt
->l_special
);
126 pn_push(path_node_t
**pnp
, char *path
)
130 if ((pn
= calloc(sizeof (path_node_t
), 1)) == NULL
)
133 if ((pn
->pn_path
= strdup(path
)) == NULL
) {
142 pn_free(path_node_t
**pnp
)
146 while (*pnp
!= NULL
) {
155 pn_free2(path_node_t
**pn1
, path_node_t
**pn2
)
162 pn_pop(path_node_t
**pnp
, char *path
)
177 (void) strlcpy(path
, pn
->pn_path
, PATH_MAX
);
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.
191 i_zone_get_zonepath(char *zone_name
, char *zonepath
, size_t rp_sz
)
193 typedef int (*zone_get_zonepath_t
)(char *, char *, size_t);
194 static zone_get_zonepath_t zone_get_zonepath_fp
= NULL
;
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
) {
203 (void) dlclose(dlhandle
);
205 zone_get_zonepath_fp
= (zone_get_zonepath_t
)sym
;
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
));
215 Pbrandname(struct ps_prochandle
*P
, char *buf
, size_t buflen
)
219 if ((addr
= Pgetauxval(P
, AT_SUN_BRANDNAME
)) == -1)
222 if (Pread_string(P
, buf
, buflen
, addr
) == -1)
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.
233 Pzonename(struct ps_prochandle
*P
, char *s
, size_t n
)
235 return (P
->ops
.pop_zonename(P
, s
, n
, P
->data
));
239 Pzoneroot(struct ps_prochandle
*P
, char *s
, size_t n
)
241 char zname
[ZONENAME_MAX
], zpath
[PATH_MAX
], tmp
[PATH_MAX
];
244 if (P
->zoneroot
!= NULL
) {
245 (void) strlcpy(s
, P
->zoneroot
, n
);
249 if ((Pzonename(P
, zname
, sizeof (zname
)) == NULL
) ||
250 (strcmp(zname
, GLOBAL_ZONENAME
) == 0)) {
251 if ((P
->zoneroot
= strdup("")) == NULL
) {
255 dprintf("Pzoneroot defaulting to '%s'\n", GLOBAL_ZONENAME
);
256 (void) strlcpy(s
, P
->zoneroot
, n
);
260 if (i_zone_get_zonepath(zname
, zpath
, sizeof (zpath
)) != Z_OK
) {
261 if ((P
->zoneroot
= strdup("")) == NULL
) {
266 "Pzoneroot zone not found '%s', defaulting to '%s'\n",
267 zname
, GLOBAL_ZONENAME
);
268 (void) strlcpy(s
, P
->zoneroot
, n
);
271 (void) strlcat(zpath
, "/root", sizeof (zpath
));
273 if ((rv
= resolvepath(zpath
, tmp
, sizeof (tmp
) - 1)) < 0) {
274 if ((P
->zoneroot
= strdup("")) == NULL
) {
279 "Pzoneroot can't access '%s:%s', defaulting to '%s'\n",
280 zname
, zpath
, GLOBAL_ZONENAME
);
281 (void) strlcpy(s
, P
->zoneroot
, n
);
285 (void) strlcpy(zpath
, tmp
, sizeof (zpath
));
287 if ((P
->zoneroot
= strdup(zpath
)) == NULL
) {
291 dprintf("Pzoneroot found zone root '%s:%s'\n", zname
, zpath
);
292 (void) strlcpy(s
, P
->zoneroot
, n
);
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
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.
311 Plofspath(const char *path
, char *s
, size_t n
)
313 char tmp
[PATH_MAX
+ 1];
319 dprintf("Plofspath path '%s'\n", path
);
321 /* We only deal with absolute paths */
325 /* Make a copy of the path so that we can muck with it */
326 (void) strlcpy(tmp
, path
, sizeof (tmp
) - 1);
329 * Use resolvepath() to make sure there are no consecutive or
330 * trailing '/'s in the path.
332 if ((rv
= resolvepath(tmp
, tmp
, sizeof (tmp
) - 1)) >= 0)
335 (void) mutex_lock(&lofs_lock
);
338 * If /etc/mnttab has been modified since the last time
339 * we looked, then rebuild the lofs lookup cache.
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
)) {
347 rebuild_lofs_cache();
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.
359 * Here's some of the variables we're going to use:
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.
367 * p - A pointer to the last '/' seen in the string.
369 * p[1] - A pointer to the component of the string we've already
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).
376 p
= &tmp
[strlen(tmp
)];
379 if ((special
= lookup_lofs_mount_point(tmp
)) != NULL
) {
380 char tmp2
[PATH_MAX
+ 1];
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
391 * However, we need to be careful to handle the case of
392 * a lofs mounted file under a lofs mounted file system.
393 * In this case, we just keep going.
396 (void) strlcpy(tmp2
, special
, sizeof (tmp2
) - 1);
397 (void) strlcat(tmp2
, "/", sizeof (tmp2
) - 1);
398 (void) strlcat(tmp2
, &p
[1], sizeof (tmp2
) - 1);
399 if ((rv
= resolvepath(tmp2
, tmp2
, sizeof (tmp2
) - 1)) >=
402 (void) strlcpy(tmp
, tmp2
, sizeof (tmp
) - 1);
403 p
= &tmp
[strlen(tmp
)];
409 /* No lofs mount found */
410 if ((p2
= strrchr(tmp
, '/')) == NULL
) {
413 (void) mutex_unlock(&lofs_lock
);
416 * We know that tmp was an absolute path, so if we
417 * made it here we know that (p == tmp) and that
418 * (*p == '\0'). This means that we've managed
419 * to check the whole path and so we're done.
422 assert(p
[0] == '\0');
424 /* Restore the leading '/' in the path */
427 if (strcmp(tmp
, path
) == 0) {
428 /* The path didn't change */
433 * It's possible that lofs source path we just
434 * obtained contains a symbolic link. Use
435 * resolvepath() to clean it up.
437 (void) strlcpy(tmp2
, tmp
, sizeof (tmp2
));
438 if ((rv
= resolvepath(tmp
, tmp
, sizeof (tmp
) - 1)) >= 0)
442 * It's always possible that our lofs source path is
443 * actually another lofs mount. So call ourselves
444 * recursively to resolve that path.
446 (void) Plofspath(tmp
, tmp
, PATH_MAX
);
448 /* Copy out our final resolved lofs source path */
449 (void) strlcpy(s
, tmp
, n
);
450 dprintf("Plofspath path result '%s'\n", s
);
455 * So the path we just checked is not a lofs mount. Next we
456 * want to check the parent path component for a lofs mount.
458 * First, restore any '/' that we replaced with a '\0' (NUL).
459 * We can determine if we should do this by looking at p[1].
460 * If p[1] points to a '\0' (NUL) then we know that p points
461 * to the end of the string and there is no '/' to restore.
462 * if p[1] doesn't point to a '\0' (NUL) then it points to
463 * the part of the path that we've already verified so there
464 * is a '/' to restore.
470 * Second, replace the last '/' in the part of the path
471 * that we've already checked with a '\0' (NUL) so that
472 * when we loop around we check the parent component of the
482 * Pzonepath() - Way too much code to attempt to derive the full path of
483 * an object within a zone.
485 * Pzonepath() takes a path and attempts to resolve it relative to the
486 * root associated with the current process handle. If it fails it will
487 * not update the results string. It is safe to specify the same pointer
488 * for the file string and the results string.
490 * Doing this resolution is more difficult than it initially sounds.
491 * We can't simply append the file path to the zone root, because in
492 * a root directory, '..' is treated the same as '.'. Also, symbolic
493 * links that specify an absolute path need to be interpreted relative
496 * It seems like perhaps we could do a chroot(<zone root>) followed by a
497 * resolvepath(). But we can't do this because chroot requires special
498 * privileges and affects the entire process. Perhaps if there was a
499 * special version of resolvepath() which took an addition root path
500 * we could use that, but this isn't ideal either. The reason is
501 * that we want to have special handling for native paths. (A native path
502 * is a path that begins with "/native/" or "/.SUNWnative/".) Native
503 * paths could be passed explicity to this function or could be embedded
504 * in a symlink that is part of the path passed into this function.
505 * These paths are always lofs mounts of global zone paths, but lofs
506 * mounts only exist when a zone is booted. So if we were to try to do
507 * a resolvepath() on a native path when the zone wasn't booted the
508 * resolvepath() would fail even though we know that the components
509 * exists in the global zone.
511 * Given all these constraints, we just implement a path walking function
512 * that resolves a file path relative to a zone root by manually inspecting
513 * each of the path components and verifying its existence. This means that
514 * we must have access to the zone and that all the components of the
515 * path must exist for this operation to succeed.
518 Pzonepath(struct ps_prochandle
*P
, const char *path
, char *s
, size_t n
)
520 char zroot
[PATH_MAX
], zpath
[PATH_MAX
], tmp
[PATH_MAX
], link
[PATH_MAX
];
521 path_node_t
*pn_stack
= NULL
, *pn_links
= NULL
, *pn
;
526 dprintf("Pzonepath lookup '%s'\n", path
);
528 /* First lookup the zone root */
529 if (Pzoneroot(P
, zroot
, sizeof (zroot
)) == NULL
)
533 * Make a temporary copy of the path specified.
534 * If it's a relative path then make it into an absolute path.
538 (void) strlcat(tmp
, "/", sizeof (tmp
));
539 (void) strlcat(tmp
, path
, sizeof (tmp
));
542 * If the path that was passed in is the zone root, we're done.
543 * If the path that was passed in already contains the zone root
544 * then strip the zone root out and verify the rest of the path.
546 if (strcmp(tmp
, zroot
) == 0) {
547 (void) Plofspath(zroot
, zroot
, sizeof (zroot
));
548 dprintf("Pzonepath found zone path (1) '%s'\n", zroot
);
549 (void) strlcpy(s
, zroot
, n
);
553 if ((strncmp(tmp
, zroot
, i
) == 0) && (tmp
[i
] == '/'))
554 (void) memmove(tmp
, tmp
+ i
, strlen(tmp
+ i
) + 1);
556 /* If no path is passed in, then it maps to the zone root */
557 if (strlen(tmp
) == 0) {
558 (void) Plofspath(zroot
, zroot
, sizeof (zroot
));
559 dprintf("Pzonepath found zone path (2) '%s'\n", zroot
);
560 (void) strlcpy(s
, zroot
, n
);
565 * Push each path component that we plan to verify onto a stack of
566 * path components, with parent components at the top of the stack.
567 * So for example, if we're going to verify the path /foo/bar/bang
568 * then our stack will look like:
573 while ((p
= strrchr(tmp
, '/')) != NULL
) {
575 if (pn_push(&pn_stack
, &p
[1]) != NULL
)
581 /* We're going to store the final zone relative path in zpath */
584 while (pn_pop(&pn_stack
, tmp
) != NULL
) {
586 * Drop zero length path components (which come from
587 * consecutive '/'s) and '.' path components.
589 if ((strlen(tmp
) == 0) || (strcmp(tmp
, ".") == 0))
593 * Check the current path component for '..', if found
594 * drop any previous path component.
596 if (strcmp(tmp
, "..") == 0) {
597 if ((p
= strrchr(zpath
, '/')) != NULL
)
602 /* The path we want to verify now is zpath + / + tmp. */
603 (void) strlcat(zpath
, "/", sizeof (zpath
));
604 (void) strlcat(zpath
, tmp
, sizeof (zpath
));
607 * Check if this is a native object. A native object is an
608 * object from the global zone that is running in a branded
609 * zone. These objects are lofs mounted into a zone. So if a
610 * branded zone is not booted then lofs mounts won't be setup
611 * so we won't be able to find these objects. Luckily, we know
612 * that they exist in the global zone with the same path sans
613 * the initial native component, so we'll just strip out the
614 * native component here.
616 if ((strncmp(zpath
, "/native", sizeof ("/native")) == 0) ||
617 (strncmp(zpath
, "/.SUNWnative",
618 sizeof ("/.SUNWnative")) == 0)) {
620 /* Free any cached symlink paths */
623 /* Reconstruct the path from our path component stack */
625 while (pn_pop(&pn_stack
, tmp
) != NULL
) {
626 (void) strlcat(zpath
, "/", sizeof (zpath
));
627 (void) strlcat(zpath
, tmp
, sizeof (zpath
));
630 /* Verify that the path actually exists */
631 rv
= resolvepath(zpath
, tmp
, sizeof (tmp
) - 1);
633 dprintf("Pzonepath invalid native path '%s'\n",
639 /* Return the path */
640 dprintf("Pzonepath found native path '%s'\n", tmp
);
641 (void) Plofspath(tmp
, tmp
, sizeof (tmp
));
642 (void) strlcpy(s
, tmp
, n
);
647 * Check if the path points to a symlink. We do this
648 * explicitly since any absolute symlink needs to be
649 * interpreted relativly to the zone root and not "/".
651 (void) strlcpy(tmp
, zroot
, sizeof (tmp
));
652 (void) strlcat(tmp
, zpath
, sizeof (tmp
));
653 if (lstat64(tmp
, &sb
) != 0) {
654 pn_free2(&pn_stack
, &pn_links
);
657 if (!S_ISLNK(sb
.st_mode
)) {
659 * Since the lstat64() above succeeded we know that
660 * zpath exists, since this is not a symlink loop
661 * around and check the next path component.
667 * Symlink allow for paths with loops. Make sure
668 * we're not stuck in a loop.
670 for (pn
= pn_links
; pn
!= NULL
; pn
= pn
->pn_next
) {
671 if (strcmp(zpath
, pn
->pn_path
) != 0)
674 /* We have a loop. Fail. */
675 dprintf("Pzonepath symlink loop '%s'\n", zpath
);
676 pn_free2(&pn_stack
, &pn_links
);
680 /* Save this symlink path for future loop checks */
681 if (pn_push(&pn_links
, zpath
) == NULL
) {
683 pn_free2(&pn_stack
, &pn_links
);
687 /* Now follow the contents of the symlink */
688 bzero(link
, sizeof (link
));
689 if (readlink(tmp
, link
, sizeof (link
)) == -1) {
690 pn_free2(&pn_stack
, &pn_links
);
694 dprintf("Pzonepath following symlink '%s' -> '%s'\n",
698 * Push each path component of the symlink target onto our
699 * path components stack since we need to verify each one.
701 while ((p
= strrchr(link
, '/')) != NULL
) {
703 if (pn_push(&pn_stack
, &p
[1]) != NULL
)
705 pn_free2(&pn_stack
, &pn_links
);
709 /* absolute or relative symlink? */
711 /* Absolute symlink, nuke existing zpath. */
717 * Relative symlink. Push the first path component of the
718 * symlink target onto our stack for verification and then
719 * remove the current path component from zpath.
721 if (pn_push(&pn_stack
, link
) == NULL
) {
722 pn_free2(&pn_stack
, &pn_links
);
725 p
= strrchr(zpath
, '/');
732 /* Place the final result in zpath */
733 (void) strlcpy(tmp
, zroot
, sizeof (tmp
));
734 (void) strlcat(tmp
, zpath
, sizeof (tmp
));
735 (void) strlcpy(zpath
, tmp
, sizeof (zpath
));
737 (void) Plofspath(zpath
, zpath
, sizeof (zpath
));
738 dprintf("Pzonepath found zone path (3) '%s'\n", zpath
);
740 (void) strlcpy(s
, zpath
, n
);
745 Pfindobj(struct ps_prochandle
*P
, const char *path
, char *s
, size_t n
)
749 dprintf("Pfindobj '%s'\n", path
);
751 /* We only deal with absolute paths */
755 /* First try to resolve the path to some zone */
756 if (Pzonepath(P
, path
, s
, n
) != NULL
)
759 /* If that fails resolve any lofs links in the path */
760 if (Plofspath(path
, s
, n
) != NULL
)
763 /* If that fails then just see if the path exists */
764 if ((len
= resolvepath(path
, s
, n
)) > 0) {
773 Pfindmap(struct ps_prochandle
*P
, map_info_t
*mptr
, char *s
, size_t n
)
775 file_info_t
*fptr
= mptr
->map_file
;
779 /* If it's already been explicity set return that */
780 if ((fptr
!= NULL
) && (fptr
->file_rname
!= NULL
)) {
781 (void) strlcpy(s
, fptr
->file_rname
, n
);
785 /* If it's the a.out segment, defer to the magical Pexecname() */
786 if ((P
->map_exec
== mptr
) ||
787 (strcmp(mptr
->map_pmap
.pr_mapname
, "a.out") == 0) ||
788 ((fptr
!= NULL
) && (fptr
->file_lname
!= NULL
) &&
789 (strcmp(fptr
->file_lname
, "a.out") == 0))) {
790 if (Pexecname(P
, buf
, sizeof (buf
)) != NULL
) {
791 (void) strlcpy(s
, buf
, n
);
796 /* Try /proc first to get the real object name */
797 if ((Pstate(P
) != PS_DEAD
) && (mptr
->map_pmap
.pr_mapname
[0] != '\0')) {
798 (void) snprintf(buf
, sizeof (buf
), "%s/%d/path/%s",
799 procfs_path
, (int)P
->pid
, mptr
->map_pmap
.pr_mapname
);
800 if ((len
= readlink(buf
, buf
, sizeof (buf
))) > 0) {
802 (void) Plofspath(buf
, buf
, sizeof (buf
));
803 (void) strlcpy(s
, buf
, n
);
809 * If we couldn't get the name from /proc, take the lname and
810 * try to expand it on the current system to a real object path.
812 fptr
= mptr
->map_file
;
813 if ((fptr
!= NULL
) && (fptr
->file_lname
!= NULL
)) {
814 (void) strlcpy(buf
, fptr
->file_lname
, sizeof (buf
));
815 if (Pfindobj(P
, buf
, buf
, sizeof (buf
)) == NULL
)
817 (void) strlcpy(s
, buf
, n
);