11676Sjpk /*
21676Sjpk * CDDL HEADER START
31676Sjpk *
41676Sjpk * The contents of this file are subject to the terms of the
51676Sjpk * Common Development and Distribution License (the "License").
61676Sjpk * You may not use this file except in compliance with the License.
71676Sjpk *
81676Sjpk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91676Sjpk * or http://www.opensolaris.org/os/licensing.
101676Sjpk * See the License for the specific language governing permissions
111676Sjpk * and limitations under the License.
121676Sjpk *
131676Sjpk * When distributing Covered Code, include this CDDL HEADER in each
141676Sjpk * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151676Sjpk * If applicable, add the following below this CDDL HEADER, with the
161676Sjpk * fields enclosed by brackets "[]" replaced with your own identifying
171676Sjpk * information: Portions Copyright [yyyy] [name of copyright owner]
181676Sjpk *
191676Sjpk * CDDL HEADER END
201676Sjpk */
211676Sjpk /*
22*4541Sjparcel * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
231676Sjpk * Use is subject to license terms.
241676Sjpk */
251676Sjpk
261676Sjpk #pragma ident "%Z%%M% %I% %E% SMI"
271676Sjpk
281676Sjpk
291676Sjpk /*
301676Sjpk * Name: getpathbylabel.c
311676Sjpk *
321676Sjpk * Description: Returns the global zone pathname corresponding
331676Sjpk * to the specified label. The pathname does
341676Sjpk * not need to match an existing file system object.
351676Sjpk *
361676Sjpk */
371676Sjpk #include <stdio.h>
381676Sjpk #include <string.h>
391676Sjpk #include <unistd.h>
401676Sjpk #include <errno.h>
411676Sjpk #include <sys/types.h>
421676Sjpk #include <tsol/label.h>
431676Sjpk #include <stdlib.h>
441676Sjpk #include <zone.h>
451676Sjpk #include <sys/mntent.h>
461676Sjpk #include <sys/mnttab.h>
471676Sjpk #include <stdarg.h>
481676Sjpk
491676Sjpk /*
501676Sjpk * This structure is used to chain mntent structures into a list
511676Sjpk * and to cache stat information for each member of the list.
521676Sjpk */
531676Sjpk struct mntlist {
541676Sjpk struct mnttab *mntl_mnt;
551676Sjpk struct mntlist *mntl_next;
561676Sjpk };
571676Sjpk
581676Sjpk
591676Sjpk /*
601676Sjpk * Return a pointer to the trailing suffix of full that follows the prefix
611676Sjpk * given by pref. If pref isn't a prefix of full, return NULL. Apply
621676Sjpk * pathname semantics to the prefix test, so that pref must match at a
631676Sjpk * component boundary.
641676Sjpk */
651676Sjpk static char *
pathsuffix(char * full,char * pref)661676Sjpk pathsuffix(char *full, char *pref)
671676Sjpk {
681676Sjpk int preflen;
691676Sjpk
701676Sjpk if (full == NULL || pref == NULL)
711676Sjpk return (NULL);
721676Sjpk
731676Sjpk preflen = strlen(pref);
741676Sjpk if (strncmp(pref, full, preflen) != 0)
751676Sjpk return (NULL);
761676Sjpk
771676Sjpk /*
781676Sjpk * pref is a substring of full. To be a subpath, it cannot cover a
791676Sjpk * partial component of full. The last clause of the test handles the
801676Sjpk * special case of the root.
811676Sjpk */
821676Sjpk if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1)
831676Sjpk return (NULL);
841676Sjpk
851676Sjpk if (preflen == 1 && full[0] == '/')
861676Sjpk return (full);
871676Sjpk else
881676Sjpk return (full + preflen);
891676Sjpk }
901676Sjpk
911676Sjpk /*
921676Sjpk * Return zero iff the path named by sub is a leading subpath
931676Sjpk * of the path named by full.
941676Sjpk *
951676Sjpk * Treat null paths as matching nothing.
961676Sjpk */
971676Sjpk static int
subpath(char * full,char * sub)981676Sjpk subpath(char *full, char *sub)
991676Sjpk {
1001676Sjpk return (pathsuffix(full, sub) == NULL);
1011676Sjpk }
1021676Sjpk
1031676Sjpk static void
tsol_mnt_free(struct mnttab * mnt)1041676Sjpk tsol_mnt_free(struct mnttab *mnt)
1051676Sjpk {
1061676Sjpk if (mnt->mnt_special)
1071676Sjpk free(mnt->mnt_special);
1081676Sjpk if (mnt->mnt_mountp)
1091676Sjpk free(mnt->mnt_mountp);
1101676Sjpk if (mnt->mnt_fstype)
1111676Sjpk free(mnt->mnt_fstype);
1121676Sjpk if (mnt->mnt_mntopts)
1131676Sjpk free(mnt->mnt_mntopts);
1141676Sjpk free(mnt);
1151676Sjpk }
1161676Sjpk
1171676Sjpk static void
tsol_mlist_free(struct mntlist * mlist)1181676Sjpk tsol_mlist_free(struct mntlist *mlist)
1191676Sjpk {
1201676Sjpk struct mntlist *mlp;
121*4541Sjparcel struct mntlist *oldmlp;
1221676Sjpk
123*4541Sjparcel mlp = mlist;
124*4541Sjparcel while (mlp) {
1251676Sjpk struct mnttab *mnt = mlp->mntl_mnt;
1261676Sjpk
1271676Sjpk if (mnt)
1281676Sjpk tsol_mnt_free(mnt);
129*4541Sjparcel oldmlp = mlp;
130*4541Sjparcel mlp = mlp->mntl_next;
131*4541Sjparcel free(oldmlp);
1321676Sjpk }
1331676Sjpk }
1341676Sjpk
1351676Sjpk static struct mnttab *
mntdup(struct mnttab * mnt)1361676Sjpk mntdup(struct mnttab *mnt)
1371676Sjpk {
1381676Sjpk struct mnttab *new;
1391676Sjpk
1401676Sjpk new = (struct mnttab *)malloc(sizeof (*new));
1411676Sjpk if (new == NULL)
1421676Sjpk return (NULL);
1431676Sjpk
1441676Sjpk new->mnt_special = NULL;
1451676Sjpk new->mnt_mountp = NULL;
1461676Sjpk new->mnt_fstype = NULL;
1471676Sjpk new->mnt_mntopts = NULL;
1481676Sjpk
1491676Sjpk new->mnt_special = strdup(mnt->mnt_special);
1501676Sjpk if (new->mnt_special == NULL) {
1511676Sjpk tsol_mnt_free(new);
1521676Sjpk return (NULL);
1531676Sjpk }
1541676Sjpk new->mnt_mountp = strdup(mnt->mnt_mountp);
1551676Sjpk if (new->mnt_mountp == NULL) {
1561676Sjpk tsol_mnt_free(new);
1571676Sjpk return (NULL);
1581676Sjpk }
1591676Sjpk new->mnt_fstype = strdup(mnt->mnt_fstype);
1601676Sjpk if (new->mnt_fstype == NULL) {
1611676Sjpk tsol_mnt_free(new);
1621676Sjpk return (NULL);
1631676Sjpk }
1641676Sjpk new->mnt_mntopts = strdup(mnt->mnt_mntopts);
1651676Sjpk if (new->mnt_mntopts == NULL) {
1661676Sjpk tsol_mnt_free(new);
1671676Sjpk return (NULL);
1681676Sjpk }
1691676Sjpk return (new);
1701676Sjpk }
1711676Sjpk
1721676Sjpk static struct mntlist *
tsol_mkmntlist(void)1731676Sjpk tsol_mkmntlist(void)
1741676Sjpk {
1751676Sjpk FILE *mounted;
1761676Sjpk struct mntlist *mntl;
1771676Sjpk struct mntlist *mntst = NULL;
1781676Sjpk struct mnttab mnt;
1791676Sjpk
1801914Scasper if ((mounted = fopen(MNTTAB, "rF")) == NULL) {
1811676Sjpk perror(MNTTAB);
1821676Sjpk return (NULL);
1831676Sjpk }
1841676Sjpk resetmnttab(mounted);
1851676Sjpk while (getmntent(mounted, &mnt) == NULL) {
1861676Sjpk mntl = (struct mntlist *)malloc(sizeof (*mntl));
1871676Sjpk if (mntl == NULL) {
1881676Sjpk tsol_mlist_free(mntst);
1891676Sjpk mntst = NULL;
1901676Sjpk break;
1911676Sjpk }
1921676Sjpk mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt));
1931676Sjpk if (mntl->mntl_mnt == NULL) {
1941676Sjpk tsol_mlist_free(mntst);
1951676Sjpk mntst = NULL;
1961676Sjpk break;
1971676Sjpk }
1981676Sjpk mntl->mntl_next = mntst;
1991676Sjpk mntst = mntl;
2001676Sjpk }
2011676Sjpk (void) fclose(mounted);
2021676Sjpk return (mntst);
2031676Sjpk }
2041676Sjpk
2051676Sjpk /*
2061676Sjpk * This function attempts to convert local zone NFS mounted pathnames
2071676Sjpk * into equivalent global zone NFS mounted pathnames. At present
2081676Sjpk * it only works for automounted filesystems. It depends on the
2091676Sjpk * assumption that both the local and global zone automounters
2101676Sjpk * share the same nameservices. It also assumes that any automount
2111676Sjpk * map used by a local zone is available to the global zone automounter.
2121676Sjpk *
2131676Sjpk * The algorithm used consists of three phases.
2141676Sjpk *
2151676Sjpk * 1. The local zone's mnttab is searched to find the automount map
2161676Sjpk * with the closest matching mountpath.
2171676Sjpk *
2181676Sjpk * 2. The matching autmount map name is looked up in the global zone's
2191676Sjpk * mnttab to determine the path where it should be mounted in the
2201676Sjpk * global zone.
2211676Sjpk *
2221676Sjpk * 3. A pathname covered by an appropiate autofs trigger mount in
2231676Sjpk * the global zone is generated as the resolved pathname
2241676Sjpk *
2251676Sjpk * Among the things that can go wrong is that global zone doesn't have
2261676Sjpk * a matching automount map or the mount was not done via the automounter.
2271676Sjpk * Either of these cases return a NULL path.
2281676Sjpk */
2291676Sjpk #define ZONE_OPT "zone="
2301676Sjpk static int
getnfspathbyautofs(struct mntlist * mlist,zoneid_t zoneid,struct mnttab * autofs_mnt,char * globalpath,char * zonepath,int global_len)2311676Sjpk getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid,
2321676Sjpk struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len)
2331676Sjpk {
2341676Sjpk struct mntlist *mlp;
2351676Sjpk char zonematch[ZONENAME_MAX + 20];
2361676Sjpk char zonename[ZONENAME_MAX];
2371676Sjpk int longestmatch;
2381676Sjpk struct mnttab *mountmatch;
2391676Sjpk
2401676Sjpk if (autofs_mnt) {
2411676Sjpk mountmatch = autofs_mnt;
2421676Sjpk longestmatch = strlen(mountmatch->mnt_mountp);
2431676Sjpk } else {
2441676Sjpk /*
2451676Sjpk * First we need to get the zonename to look for
2461676Sjpk */
2471676Sjpk if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename,
2481676Sjpk ZONENAME_MAX) == -1) {
2491676Sjpk return (0);
2501676Sjpk }
2511676Sjpk
2521676Sjpk (void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch));
2531676Sjpk (void) strlcat(zonematch, zonename, sizeof (zonematch));
2541676Sjpk
2551676Sjpk /*
2561676Sjpk * Find the best match for an automount map that
2571676Sjpk * corresponds to the local zone's pathname
2581676Sjpk */
2591676Sjpk longestmatch = 0;
2601676Sjpk for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
2611676Sjpk struct mnttab *mnt = mlp->mntl_mnt;
2621676Sjpk int len;
2631676Sjpk int matchfound;
2641676Sjpk char *token;
2651676Sjpk char *lasts;
2661676Sjpk char mntopts[MAXPATHLEN];
2671676Sjpk
2681676Sjpk if (subpath(globalpath, mnt->mnt_mountp) != 0)
2691676Sjpk continue;
2701676Sjpk if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
2711676Sjpk continue;
2721676Sjpk
2731676Sjpk matchfound = 0;
2741676Sjpk (void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN);
2751676Sjpk if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) {
2761676Sjpk if (strcmp(token, zonematch) == 0) {
2771676Sjpk matchfound = 1;
2781676Sjpk } else while ((token = strtok_r(NULL, ",",
2791676Sjpk &lasts)) != NULL) {
2801676Sjpk if (strcmp(token, zonematch) == 0) {
2811676Sjpk matchfound = 1;
2821676Sjpk break;
2831676Sjpk }
2841676Sjpk }
2851676Sjpk }
2861676Sjpk if (matchfound) {
2871676Sjpk len = strlen(mnt->mnt_mountp);
2881676Sjpk if (len > longestmatch) {
2891676Sjpk mountmatch = mnt;
2901676Sjpk longestmatch = len;
2911676Sjpk }
2921676Sjpk }
2931676Sjpk }
2941676Sjpk }
2951676Sjpk if (longestmatch == 0) {
2961676Sjpk return (0);
2971676Sjpk } else {
2981676Sjpk /*
2991676Sjpk * Now we may have found the corresponding autofs mount
3001676Sjpk * Try to find the matching global zone autofs entry
3011676Sjpk */
3021676Sjpk
3031676Sjpk for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
3041676Sjpk char p[MAXPATHLEN];
3051676Sjpk size_t zp_len;
3061676Sjpk size_t mp_len;
3071676Sjpk
3081676Sjpk struct mnttab *mnt = mlp->mntl_mnt;
3091676Sjpk
3101676Sjpk if (strcmp(mountmatch->mnt_special,
3111676Sjpk mnt->mnt_special) != 0)
3121676Sjpk continue;
3131676Sjpk if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
3141676Sjpk continue;
3151676Sjpk if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL)
3161676Sjpk continue;
3171676Sjpk /*
3181676Sjpk * OK, we have a matching global zone automap
3191676Sjpk * so adjust the path for the global zone.
3201676Sjpk */
3211676Sjpk zp_len = strlen(zonepath);
3221676Sjpk mp_len = strlen(mnt->mnt_mountp);
3231676Sjpk (void) strncpy(p, globalpath + zp_len, MAXPATHLEN);
3241676Sjpk /*
3251676Sjpk * If both global zone and zone-relative
3261676Sjpk * mountpoint match, just use the same pathname
3271676Sjpk */
3281676Sjpk if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) {
3291676Sjpk (void) strncpy(globalpath, p, global_len);
3301676Sjpk return (1);
3311676Sjpk } else {
3321676Sjpk (void) strncpy(p, globalpath, MAXPATHLEN);
3331676Sjpk (void) strncpy(globalpath, mnt->mnt_mountp,
3341676Sjpk global_len);
3351676Sjpk (void) strlcat(globalpath,
3361676Sjpk p + strlen(mountmatch->mnt_mountp),
3371676Sjpk global_len);
3381676Sjpk return (1);
3391676Sjpk }
3401676Sjpk }
3411676Sjpk return (0);
3421676Sjpk }
3431676Sjpk }
3441676Sjpk
345*4541Sjparcel /*
346*4541Sjparcel * Find the pathname for the entry in mlist that corresponds to the
347*4541Sjparcel * file named by path (i.e., that names a mount table entry for the
348*4541Sjparcel * file system in which path lies).
349*4541Sjparcel *
350*4541Sjparcel * Return 0 is there an error.
351*4541Sjparcel */
352*4541Sjparcel static int
getglobalpath(const char * path,zoneid_t zoneid,struct mntlist * mlist,char * globalpath)353*4541Sjparcel getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist,
354*4541Sjparcel char *globalpath)
355*4541Sjparcel {
356*4541Sjparcel struct mntlist *mlp;
357*4541Sjparcel char lofspath[MAXPATHLEN];
358*4541Sjparcel char zonepath[MAXPATHLEN];
359*4541Sjparcel int longestmatch;
360*4541Sjparcel struct mnttab *mountmatch;
3611676Sjpk
362*4541Sjparcel if (zoneid != GLOBAL_ZONEID) {
363*4541Sjparcel char *prefix;
3641676Sjpk
365*4541Sjparcel if ((prefix = getzonerootbyid(zoneid)) == NULL) {
366*4541Sjparcel return (0);
3671676Sjpk }
368*4541Sjparcel (void) strncpy(zonepath, prefix, MAXPATHLEN);
369*4541Sjparcel (void) strlcpy(globalpath, prefix, MAXPATHLEN);
370*4541Sjparcel (void) strlcat(globalpath, path, MAXPATHLEN);
371*4541Sjparcel free(prefix);
372*4541Sjparcel } else {
373*4541Sjparcel (void) strlcpy(globalpath, path, MAXPATHLEN);
374*4541Sjparcel }
3751676Sjpk
376*4541Sjparcel for (;;) {
377*4541Sjparcel longestmatch = 0;
378*4541Sjparcel for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
379*4541Sjparcel struct mnttab *mnt = mlp->mntl_mnt;
380*4541Sjparcel int len;
3811676Sjpk
3821676Sjpk if (subpath(globalpath, mnt->mnt_mountp) != 0)
3831676Sjpk continue;
3841676Sjpk len = strlen(mnt->mnt_mountp);
3851676Sjpk if (len > longestmatch) {
3861676Sjpk mountmatch = mnt;
3871676Sjpk longestmatch = len;
3881676Sjpk }
3891676Sjpk }
3901676Sjpk /*
3911676Sjpk * Handle interesting mounts.
3921676Sjpk */
3931676Sjpk if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) ||
3941676Sjpk (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) {
3951676Sjpk if (zoneid > GLOBAL_ZONEID) {
3961676Sjpk struct mnttab *m = NULL;
3971676Sjpk
3981676Sjpk if (strcmp(mountmatch->mnt_fstype,
3991676Sjpk MNTTYPE_AUTOFS) == 0)
4001676Sjpk m = mountmatch;
4011676Sjpk if (getnfspathbyautofs(mlist, zoneid, m,
4021676Sjpk globalpath, zonepath, MAXPATHLEN) == 0) {
4031676Sjpk return (0);
4041676Sjpk }
4051676Sjpk }
4061676Sjpk break;
4071676Sjpk } else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) {
4081676Sjpk /*
4091676Sjpk * count up what's left
4101676Sjpk */
4111676Sjpk int remainder;
4121676Sjpk
4131676Sjpk remainder = strlen(globalpath) - longestmatch;
4141676Sjpk if (remainder > 0) {
4151676Sjpk path = pathsuffix(globalpath,
4161676Sjpk mountmatch->mnt_mountp);
4171676Sjpk (void) strlcpy(lofspath, path, MAXPATHLEN);
4181676Sjpk }
4191676Sjpk (void) strlcpy(globalpath, mountmatch->mnt_special,
4201676Sjpk MAXPATHLEN);
4211676Sjpk if (remainder > 0) {
4221676Sjpk (void) strlcat(globalpath, lofspath,
4231676Sjpk MAXPATHLEN);
4241676Sjpk }
4251676Sjpk } else {
4261676Sjpk if ((zoneid > GLOBAL_ZONEID) &&
4271676Sjpk (strncmp(path, "/home/", strlen("/home/")) == 0)) {
4281676Sjpk char zonename[ZONENAME_MAX];
4291676Sjpk
4301676Sjpk /*
4311676Sjpk * If this is a cross-zone reference to
4321676Sjpk * a home directory, it must be corrected.
4331676Sjpk * We should only get here if the zone's
4341676Sjpk * automounter hasn't yet mounted its
4351676Sjpk * autofs trigger on /home.
4361676Sjpk *
4371676Sjpk * Since it is likely to do so in the
4381676Sjpk * future, we will assume that the global
4391676Sjpk * zone already has an equivalent autofs
4401676Sjpk * mount established. By convention,
4411676Sjpk * this should be mounted at the
4421676Sjpk * /zone/<zonename>
4431676Sjpk */
4441676Sjpk
4451676Sjpk if (zone_getattr(zoneid, ZONE_ATTR_NAME,
4461676Sjpk zonename, ZONENAME_MAX) == -1) {
4471676Sjpk return (0);
4481676Sjpk } else {
4491676Sjpk (void) snprintf(globalpath, MAXPATHLEN,
4501676Sjpk "/zone/%s%s", zonename, path);
4511676Sjpk }
4521676Sjpk }
4531676Sjpk break;
4541676Sjpk }
4551676Sjpk }
4561676Sjpk return (1);
4571676Sjpk }
4581676Sjpk
4591676Sjpk
4601676Sjpk /*
4611676Sjpk * This function is only useful for global zone callers
4621676Sjpk * It uses the global zone mnttab to translate local zone pathnames
4631676Sjpk * into global zone pathnames.
4641676Sjpk */
4651676Sjpk char *
getpathbylabel(const char * path_name,char * resolved_path,size_t bufsize,const bslabel_t * sl)4661676Sjpk getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize,
467*4541Sjparcel const bslabel_t *sl)
468*4541Sjparcel {
4691676Sjpk char ret_path[MAXPATHLEN]; /* pathname to return */
4701676Sjpk zoneid_t zoneid;
4711676Sjpk struct mntlist *mlist;
4721676Sjpk
4731676Sjpk if (getzoneid() != GLOBAL_ZONEID) {
4741676Sjpk errno = EINVAL;
4751676Sjpk return (NULL);
4761676Sjpk }
4771676Sjpk
4781676Sjpk if (path_name[0] != '/') { /* need absolute pathname */
4791676Sjpk errno = EINVAL;
4801676Sjpk return (NULL);
4811676Sjpk }
4821676Sjpk
4831676Sjpk if (resolved_path == NULL) {
4841676Sjpk errno = EINVAL;
4851676Sjpk return (NULL);
4861676Sjpk }
4871676Sjpk
4881676Sjpk if ((zoneid = getzoneidbylabel(sl)) == -1)
4891676Sjpk return (NULL);
4901676Sjpk
4911676Sjpk /*
4921676Sjpk * Construct the list of mounted file systems.
4931676Sjpk */
4941676Sjpk
4951676Sjpk if ((mlist = tsol_mkmntlist()) == NULL) {
4961676Sjpk return (NULL);
4971676Sjpk }
4981676Sjpk if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) {
4991676Sjpk tsol_mlist_free(mlist);
5001676Sjpk return (NULL);
5011676Sjpk }
5021676Sjpk tsol_mlist_free(mlist);
5031676Sjpk if (strlen(ret_path) >= bufsize) {
5041676Sjpk errno = EFAULT;
5051676Sjpk return (NULL);
5061676Sjpk }
5071676Sjpk return (strcpy(resolved_path, ret_path));
5081676Sjpk } /* end getpathbylabel() */
509