| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| |
| /* |
| * Name: getpathbylabel.c |
| * |
| * Description: Returns the global zone pathname corresponding |
| * to the specified label. The pathname does |
| * not need to match an existing file system object. |
| * |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <tsol/label.h> |
| #include <stdlib.h> |
| #include <zone.h> |
| #include <sys/mntent.h> |
| #include <sys/mnttab.h> |
| #include <stdarg.h> |
| |
| /* |
| * This structure is used to chain mntent structures into a list |
| * and to cache stat information for each member of the list. |
| */ |
| struct mntlist { |
| struct mnttab *mntl_mnt; |
| struct mntlist *mntl_next; |
| }; |
| |
| |
| /* |
| * Return a pointer to the trailing suffix of full that follows the prefix |
| * given by pref. If pref isn't a prefix of full, return NULL. Apply |
| * pathname semantics to the prefix test, so that pref must match at a |
| * component boundary. |
| */ |
| static char * |
| pathsuffix(char *full, char *pref) |
| { |
| int preflen; |
| |
| if (full == NULL || pref == NULL) |
| return (NULL); |
| |
| preflen = strlen(pref); |
| if (strncmp(pref, full, preflen) != 0) |
| return (NULL); |
| |
| /* |
| * pref is a substring of full. To be a subpath, it cannot cover a |
| * partial component of full. The last clause of the test handles the |
| * special case of the root. |
| */ |
| if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1) |
| return (NULL); |
| |
| if (preflen == 1 && full[0] == '/') |
| return (full); |
| else |
| return (full + preflen); |
| } |
| |
| /* |
| * Return zero iff the path named by sub is a leading subpath |
| * of the path named by full. |
| * |
| * Treat null paths as matching nothing. |
| */ |
| static int |
| subpath(char *full, char *sub) |
| { |
| return (pathsuffix(full, sub) == NULL); |
| } |
| |
| static void |
| tsol_mnt_free(struct mnttab *mnt) |
| { |
| if (mnt->mnt_special) |
| free(mnt->mnt_special); |
| if (mnt->mnt_mountp) |
| free(mnt->mnt_mountp); |
| if (mnt->mnt_fstype) |
| free(mnt->mnt_fstype); |
| if (mnt->mnt_mntopts) |
| free(mnt->mnt_mntopts); |
| free(mnt); |
| } |
| |
| static void |
| tsol_mlist_free(struct mntlist *mlist) |
| { |
| struct mntlist *mlp; |
| |
| for (mlp = mlist; mlp; mlp = mlp->mntl_next) { |
| struct mnttab *mnt = mlp->mntl_mnt; |
| |
| if (mnt) |
| tsol_mnt_free(mnt); |
| free(mlp); |
| } |
| } |
| |
| static struct mnttab * |
| mntdup(struct mnttab *mnt) |
| { |
| struct mnttab *new; |
| |
| new = (struct mnttab *)malloc(sizeof (*new)); |
| if (new == NULL) |
| return (NULL); |
| |
| new->mnt_special = NULL; |
| new->mnt_mountp = NULL; |
| new->mnt_fstype = NULL; |
| new->mnt_mntopts = NULL; |
| |
| new->mnt_special = strdup(mnt->mnt_special); |
| if (new->mnt_special == NULL) { |
| tsol_mnt_free(new); |
| return (NULL); |
| } |
| new->mnt_mountp = strdup(mnt->mnt_mountp); |
| if (new->mnt_mountp == NULL) { |
| tsol_mnt_free(new); |
| return (NULL); |
| } |
| new->mnt_fstype = strdup(mnt->mnt_fstype); |
| if (new->mnt_fstype == NULL) { |
| tsol_mnt_free(new); |
| return (NULL); |
| } |
| new->mnt_mntopts = strdup(mnt->mnt_mntopts); |
| if (new->mnt_mntopts == NULL) { |
| tsol_mnt_free(new); |
| return (NULL); |
| } |
| return (new); |
| } |
| |
| static struct mntlist * |
| tsol_mkmntlist(void) |
| { |
| FILE *mounted; |
| struct mntlist *mntl; |
| struct mntlist *mntst = NULL; |
| struct mnttab mnt; |
| |
| if ((mounted = fopen(MNTTAB, "r")) == NULL) { |
| perror(MNTTAB); |
| return (NULL); |
| } |
| resetmnttab(mounted); |
| while (getmntent(mounted, &mnt) == NULL) { |
| mntl = (struct mntlist *)malloc(sizeof (*mntl)); |
| if (mntl == NULL) { |
| tsol_mlist_free(mntst); |
| mntst = NULL; |
| break; |
| } |
| mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt)); |
| if (mntl->mntl_mnt == NULL) { |
| tsol_mlist_free(mntst); |
| mntst = NULL; |
| break; |
| } |
| mntl->mntl_next = mntst; |
| mntst = mntl; |
| } |
| (void) fclose(mounted); |
| return (mntst); |
| } |
| |
| /* |
| * This function attempts to convert local zone NFS mounted pathnames |
| * into equivalent global zone NFS mounted pathnames. At present |
| * it only works for automounted filesystems. It depends on the |
| * assumption that both the local and global zone automounters |
| * share the same nameservices. It also assumes that any automount |
| * map used by a local zone is available to the global zone automounter. |
| * |
| * The algorithm used consists of three phases. |
| * |
| * 1. The local zone's mnttab is searched to find the automount map |
| * with the closest matching mountpath. |
| * |
| * 2. The matching autmount map name is looked up in the global zone's |
| * mnttab to determine the path where it should be mounted in the |
| * global zone. |
| * |
| * 3. A pathname covered by an appropiate autofs trigger mount in |
| * the global zone is generated as the resolved pathname |
| * |
| * Among the things that can go wrong is that global zone doesn't have |
| * a matching automount map or the mount was not done via the automounter. |
| * Either of these cases return a NULL path. |
| */ |
| #define ZONE_OPT "zone=" |
| static int |
| getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid, |
| struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len) |
| { |
| struct mntlist *mlp; |
| char zonematch[ZONENAME_MAX + 20]; |
| char zonename[ZONENAME_MAX]; |
| int longestmatch; |
| struct mnttab *mountmatch; |
| |
| if (autofs_mnt) { |
| mountmatch = autofs_mnt; |
| longestmatch = strlen(mountmatch->mnt_mountp); |
| } else { |
| /* |
| * First we need to get the zonename to look for |
| */ |
| if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename, |
| ZONENAME_MAX) == -1) { |
| return (0); |
| } |
| |
| (void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch)); |
| (void) strlcat(zonematch, zonename, sizeof (zonematch)); |
| |
| /* |
| * Find the best match for an automount map that |
| * corresponds to the local zone's pathname |
| */ |
| longestmatch = 0; |
| for (mlp = mlist; mlp; mlp = mlp->mntl_next) { |
| struct mnttab *mnt = mlp->mntl_mnt; |
| int len; |
| int matchfound; |
| char *token; |
| char *lasts; |
| char mntopts[MAXPATHLEN]; |
| |
| if (subpath(globalpath, mnt->mnt_mountp) != 0) |
| continue; |
| if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) |
| continue; |
| |
| matchfound = 0; |
| (void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN); |
| if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) { |
| if (strcmp(token, zonematch) == 0) { |
| matchfound = 1; |
| } else while ((token = strtok_r(NULL, ",", |
| &lasts)) != NULL) { |
| if (strcmp(token, zonematch) == 0) { |
| matchfound = 1; |
| break; |
| } |
| } |
| } |
| if (matchfound) { |
| len = strlen(mnt->mnt_mountp); |
| if (len > longestmatch) { |
| mountmatch = mnt; |
| longestmatch = len; |
| } |
| } |
| } |
| } |
| if (longestmatch == 0) { |
| return (0); |
| } else { |
| /* |
| * Now we may have found the corresponding autofs mount |
| * Try to find the matching global zone autofs entry |
| */ |
| |
| for (mlp = mlist; mlp; mlp = mlp->mntl_next) { |
| char p[MAXPATHLEN]; |
| size_t zp_len; |
| size_t mp_len; |
| |
| struct mnttab *mnt = mlp->mntl_mnt; |
| |
| if (strcmp(mountmatch->mnt_special, |
| mnt->mnt_special) != 0) |
| continue; |
| if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) |
| continue; |
| if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL) |
| continue; |
| /* |
| * OK, we have a matching global zone automap |
| * so adjust the path for the global zone. |
| */ |
| zp_len = strlen(zonepath); |
| mp_len = strlen(mnt->mnt_mountp); |
| (void) strncpy(p, globalpath + zp_len, MAXPATHLEN); |
| /* |
| * If both global zone and zone-relative |
| * mountpoint match, just use the same pathname |
| */ |
| if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) { |
| (void) strncpy(globalpath, p, global_len); |
| return (1); |
| } else { |
| (void) strncpy(p, globalpath, MAXPATHLEN); |
| (void) strncpy(globalpath, mnt->mnt_mountp, |
| global_len); |
| (void) strlcat(globalpath, |
| p + strlen(mountmatch->mnt_mountp), |
| global_len); |
| return (1); |
| } |
| } |
| return (0); |
| } |
| } |
| |
| /* |
| * Find the pathname for the entry in mlist that corresponds to the |
| * file named by path (i.e., that names a mount table entry for the |
| * file system in which path lies). |
| * |
| * Return 0 is there an error. |
| */ |
| static int |
| getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist, |
| char *globalpath) |
| { |
| struct mntlist *mlp; |
| char lofspath[MAXPATHLEN]; |
| char zonepath[MAXPATHLEN]; |
| int longestmatch; |
| struct mnttab *mountmatch; |
| |
| if (zoneid != GLOBAL_ZONEID) { |
| char *prefix; |
| |
| if ((prefix = getzonerootbyid(zoneid)) == NULL) { |
| return (0); |
| } |
| (void) strncpy(zonepath, prefix, MAXPATHLEN); |
| (void) strlcpy(globalpath, prefix, MAXPATHLEN); |
| (void) strlcat(globalpath, path, MAXPATHLEN); |
| free(prefix); |
| } else { |
| (void) strlcpy(globalpath, path, MAXPATHLEN); |
| } |
| |
| for (;;) { |
| longestmatch = 0; |
| for (mlp = mlist; mlp; mlp = mlp->mntl_next) { |
| struct mnttab *mnt = mlp->mntl_mnt; |
| int len; |
| |
| if (subpath(globalpath, mnt->mnt_mountp) != 0) |
| continue; |
| len = strlen(mnt->mnt_mountp); |
| if (len > longestmatch) { |
| mountmatch = mnt; |
| longestmatch = len; |
| } |
| } |
| /* |
| * Handle interesting mounts. |
| */ |
| if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) || |
| (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) { |
| if (zoneid > GLOBAL_ZONEID) { |
| struct mnttab *m = NULL; |
| |
| if (strcmp(mountmatch->mnt_fstype, |
| MNTTYPE_AUTOFS) == 0) |
| m = mountmatch; |
| if (getnfspathbyautofs(mlist, zoneid, m, |
| globalpath, zonepath, MAXPATHLEN) == 0) { |
| return (0); |
| } |
| } |
| break; |
| } else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) { |
| /* |
| * count up what's left |
| */ |
| int remainder; |
| |
| remainder = strlen(globalpath) - longestmatch; |
| if (remainder > 0) { |
| path = pathsuffix(globalpath, |
| mountmatch->mnt_mountp); |
| (void) strlcpy(lofspath, path, MAXPATHLEN); |
| } |
| (void) strlcpy(globalpath, mountmatch->mnt_special, |
| MAXPATHLEN); |
| if (remainder > 0) { |
| (void) strlcat(globalpath, lofspath, |
| MAXPATHLEN); |
| } |
| } else { |
| if ((zoneid > GLOBAL_ZONEID) && |
| (strncmp(path, "/home/", strlen("/home/")) == 0)) { |
| char zonename[ZONENAME_MAX]; |
| |
| /* |
| * If this is a cross-zone reference to |
| * a home directory, it must be corrected. |
| * We should only get here if the zone's |
| * automounter hasn't yet mounted its |
| * autofs trigger on /home. |
| * |
| * Since it is likely to do so in the |
| * future, we will assume that the global |
| * zone already has an equivalent autofs |
| * mount established. By convention, |
| * this should be mounted at the |
| * /zone/<zonename> |
| */ |
| |
| if (zone_getattr(zoneid, ZONE_ATTR_NAME, |
| zonename, ZONENAME_MAX) == -1) { |
| return (0); |
| } else { |
| (void) snprintf(globalpath, MAXPATHLEN, |
| "/zone/%s%s", zonename, path); |
| } |
| } |
| break; |
| } |
| } |
| return (1); |
| } |
| |
| |
| /* |
| * This function is only useful for global zone callers |
| * It uses the global zone mnttab to translate local zone pathnames |
| * into global zone pathnames. |
| */ |
| char * |
| getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize, |
| const bslabel_t *sl) { |
| char ret_path[MAXPATHLEN]; /* pathname to return */ |
| zoneid_t zoneid; |
| struct mntlist *mlist; |
| |
| if (getzoneid() != GLOBAL_ZONEID) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| if (path_name[0] != '/') { /* need absolute pathname */ |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| if (resolved_path == NULL) { |
| errno = EINVAL; |
| return (NULL); |
| } |
| |
| if ((zoneid = getzoneidbylabel(sl)) == -1) |
| return (NULL); |
| |
| /* |
| * Construct the list of mounted file systems. |
| */ |
| |
| if ((mlist = tsol_mkmntlist()) == NULL) { |
| return (NULL); |
| } |
| if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) { |
| tsol_mlist_free(mlist); |
| return (NULL); |
| } |
| tsol_mlist_free(mlist); |
| if (strlen(ret_path) >= bufsize) { |
| errno = EFAULT; |
| return (NULL); |
| } |
| return (strcpy(resolved_path, ret_path)); |
| } /* end getpathbylabel() */ |