Home | History | Annotate | Download | only in common
      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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 
     29 /*
     30  *	Name:		getpathbylabel.c
     31  *
     32  *	Description:	Returns the global zone pathname corresponding
     33  *			to the specified label. The pathname does
     34  *			not need to match an existing file system object.
     35  *
     36  */
     37 #include <stdio.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 #include <errno.h>
     41 #include <sys/types.h>
     42 #include <tsol/label.h>
     43 #include <stdlib.h>
     44 #include <zone.h>
     45 #include <sys/mntent.h>
     46 #include <sys/mnttab.h>
     47 #include <stdarg.h>
     48 
     49 /*
     50  * This structure is used to chain mntent structures into a list
     51  * and to cache stat information for each member of the list.
     52  */
     53 struct mntlist {
     54 	struct mnttab	*mntl_mnt;
     55 	struct mntlist	*mntl_next;
     56 };
     57 
     58 
     59 /*
     60  * Return a pointer to the trailing suffix of full that follows the prefix
     61  * given by pref.  If pref isn't a prefix of full, return NULL.  Apply
     62  * pathname semantics to the prefix test, so that pref must match at a
     63  * component boundary.
     64  */
     65 static char *
     66 pathsuffix(char *full, char *pref)
     67 {
     68 	int preflen;
     69 
     70 	if (full == NULL || pref == NULL)
     71 		return (NULL);
     72 
     73 	preflen = strlen(pref);
     74 	if (strncmp(pref, full, preflen) != 0)
     75 		return (NULL);
     76 
     77 	/*
     78 	 * pref is a substring of full.  To be a subpath, it cannot cover a
     79 	 * partial component of full.  The last clause of the test handles the
     80 	 * special case of the root.
     81 	 */
     82 	if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1)
     83 		return (NULL);
     84 
     85 	if (preflen == 1 && full[0] == '/')
     86 		return (full);
     87 	else
     88 		return (full + preflen);
     89 }
     90 
     91 /*
     92  * Return zero iff the path named by sub is a leading subpath
     93  * of the path named by full.
     94  *
     95  * Treat null paths as matching nothing.
     96  */
     97 static int
     98 subpath(char *full, char *sub)
     99 {
    100 	return (pathsuffix(full, sub) == NULL);
    101 }
    102 
    103 static void
    104 tsol_mnt_free(struct mnttab *mnt)
    105 {
    106 	if (mnt->mnt_special)
    107 		free(mnt->mnt_special);
    108 	if (mnt->mnt_mountp)
    109 		free(mnt->mnt_mountp);
    110 	if (mnt->mnt_fstype)
    111 		free(mnt->mnt_fstype);
    112 	if (mnt->mnt_mntopts)
    113 		free(mnt->mnt_mntopts);
    114 	free(mnt);
    115 }
    116 
    117 static void
    118 tsol_mlist_free(struct mntlist *mlist)
    119 {
    120 	struct mntlist *mlp;
    121 	struct mntlist *oldmlp;
    122 
    123 	mlp = mlist;
    124 	while (mlp) {
    125 		struct mnttab *mnt = mlp->mntl_mnt;
    126 
    127 		if (mnt)
    128 			tsol_mnt_free(mnt);
    129 		oldmlp = mlp;
    130 		mlp = mlp->mntl_next;
    131 		free(oldmlp);
    132 	}
    133 }
    134 
    135 static struct mnttab *
    136 mntdup(struct mnttab *mnt)
    137 {
    138 	struct mnttab *new;
    139 
    140 	new = (struct mnttab *)malloc(sizeof (*new));
    141 	if (new == NULL)
    142 		return (NULL);
    143 
    144 	new->mnt_special = NULL;
    145 	new->mnt_mountp = NULL;
    146 	new->mnt_fstype = NULL;
    147 	new->mnt_mntopts = NULL;
    148 
    149 	new->mnt_special = strdup(mnt->mnt_special);
    150 	if (new->mnt_special == NULL) {
    151 		tsol_mnt_free(new);
    152 		return (NULL);
    153 	}
    154 	new->mnt_mountp = strdup(mnt->mnt_mountp);
    155 	if (new->mnt_mountp == NULL) {
    156 		tsol_mnt_free(new);
    157 		return (NULL);
    158 	}
    159 	new->mnt_fstype = strdup(mnt->mnt_fstype);
    160 	if (new->mnt_fstype == NULL) {
    161 		tsol_mnt_free(new);
    162 		return (NULL);
    163 	}
    164 	new->mnt_mntopts = strdup(mnt->mnt_mntopts);
    165 	if (new->mnt_mntopts == NULL) {
    166 		tsol_mnt_free(new);
    167 		return (NULL);
    168 	}
    169 	return (new);
    170 }
    171 
    172 static struct mntlist *
    173 tsol_mkmntlist(void)
    174 {
    175 	FILE *mounted;
    176 	struct mntlist *mntl;
    177 	struct mntlist *mntst = NULL;
    178 	struct mnttab mnt;
    179 
    180 	if ((mounted = fopen(MNTTAB, "rF")) == NULL) {
    181 		perror(MNTTAB);
    182 		return (NULL);
    183 	}
    184 	resetmnttab(mounted);
    185 	while (getmntent(mounted, &mnt) == NULL) {
    186 		mntl = (struct mntlist *)malloc(sizeof (*mntl));
    187 		if (mntl == NULL) {
    188 			tsol_mlist_free(mntst);
    189 			mntst = NULL;
    190 			break;
    191 		}
    192 		mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt));
    193 		if (mntl->mntl_mnt == NULL) {
    194 			tsol_mlist_free(mntst);
    195 			mntst = NULL;
    196 			break;
    197 		}
    198 		mntl->mntl_next = mntst;
    199 		mntst = mntl;
    200 	}
    201 	(void) fclose(mounted);
    202 	return (mntst);
    203 }
    204 
    205 /*
    206  * This function attempts to convert local zone NFS mounted pathnames
    207  * into equivalent global zone NFS mounted pathnames. At present
    208  * it only works for automounted filesystems. It depends on the
    209  * assumption that both the local and global zone automounters
    210  * share the same nameservices. It also assumes that any automount
    211  * map used by a local zone is available to the global zone automounter.
    212  *
    213  * The algorithm used consists of three phases.
    214  *
    215  * 1. The local zone's mnttab is searched to find the automount map
    216  *    with the closest matching mountpath.
    217  *
    218  * 2. The matching autmount map name is looked up in the global zone's
    219  *    mnttab to determine the path where it should be mounted in the
    220  *    global zone.
    221  *
    222  * 3. A pathname covered by an appropiate autofs trigger mount in
    223  *    the global zone is generated as the resolved pathname
    224  *
    225  * Among the things that can go wrong is that global zone doesn't have
    226  * a matching automount map or the mount was not done via the automounter.
    227  * Either of these cases return a NULL path.
    228  */
    229 #define	ZONE_OPT "zone="
    230 static int
    231 getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid,
    232     struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len)
    233 {
    234 	struct mntlist *mlp;
    235 	char zonematch[ZONENAME_MAX + 20];
    236 	char zonename[ZONENAME_MAX];
    237 	int  longestmatch;
    238 	struct	mnttab	*mountmatch;
    239 
    240 	if (autofs_mnt) {
    241 		mountmatch = autofs_mnt;
    242 		longestmatch = strlen(mountmatch->mnt_mountp);
    243 	} else {
    244 		/*
    245 		 * First we need to get the zonename to look for
    246 		 */
    247 		if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename,
    248 		    ZONENAME_MAX) == -1) {
    249 			return (0);
    250 		}
    251 
    252 		(void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch));
    253 		(void) strlcat(zonematch, zonename, sizeof (zonematch));
    254 
    255 		/*
    256 		 * Find the best match for an automount map that
    257 		 * corresponds to the local zone's pathname
    258 		 */
    259 		longestmatch = 0;
    260 		for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
    261 			struct mnttab *mnt = mlp->mntl_mnt;
    262 			int	len;
    263 			int	matchfound;
    264 			char	*token;
    265 			char	*lasts;
    266 			char	mntopts[MAXPATHLEN];
    267 
    268 			if (subpath(globalpath, mnt->mnt_mountp) != 0)
    269 				continue;
    270 			if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
    271 				continue;
    272 
    273 			matchfound = 0;
    274 			(void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN);
    275 			if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) {
    276 				if (strcmp(token, zonematch) == 0) {
    277 					matchfound = 1;
    278 				} else while ((token = strtok_r(NULL, ",",
    279 				    &lasts)) != NULL) {
    280 					if (strcmp(token, zonematch) == 0) {
    281 						matchfound = 1;
    282 						break;
    283 					}
    284 				}
    285 			}
    286 			if (matchfound) {
    287 				len = strlen(mnt->mnt_mountp);
    288 				if (len > longestmatch) {
    289 					mountmatch = mnt;
    290 					longestmatch = len;
    291 				}
    292 			}
    293 		}
    294 	}
    295 	if (longestmatch == 0) {
    296 		return (0);
    297 	} else {
    298 		/*
    299 		 * Now we may have found the corresponding autofs mount
    300 		 * Try to find the matching global zone autofs entry
    301 		 */
    302 
    303 		for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
    304 			char p[MAXPATHLEN];
    305 			size_t zp_len;
    306 			size_t mp_len;
    307 
    308 			struct mnttab *mnt = mlp->mntl_mnt;
    309 
    310 			if (strcmp(mountmatch->mnt_special,
    311 			    mnt->mnt_special) != 0)
    312 				continue;
    313 			if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
    314 				continue;
    315 			if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL)
    316 				continue;
    317 			/*
    318 			 * OK, we have a matching global zone automap
    319 			 * so adjust the path for the global zone.
    320 			 */
    321 			zp_len = strlen(zonepath);
    322 			mp_len = strlen(mnt->mnt_mountp);
    323 			(void) strncpy(p, globalpath + zp_len, MAXPATHLEN);
    324 			/*
    325 			 * If both global zone and zone-relative
    326 			 * mountpoint match, just use the same pathname
    327 			 */
    328 			if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) {
    329 				(void) strncpy(globalpath, p, global_len);
    330 				return (1);
    331 			} else {
    332 				(void) strncpy(p, globalpath, MAXPATHLEN);
    333 				(void) strncpy(globalpath, mnt->mnt_mountp,
    334 				    global_len);
    335 				(void) strlcat(globalpath,
    336 				    p + strlen(mountmatch->mnt_mountp),
    337 				    global_len);
    338 				return (1);
    339 			}
    340 		}
    341 		return (0);
    342 	}
    343 }
    344 
    345 /*
    346  * Find the pathname for the entry in mlist that corresponds to the
    347  * file named by path (i.e., that names a mount table entry for the
    348  * file system in which path lies).
    349  *
    350  * Return 0 is there an error.
    351  */
    352 static int
    353 getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist,
    354     char *globalpath)
    355 {
    356 	struct mntlist *mlp;
    357 	char		lofspath[MAXPATHLEN];
    358 	char		zonepath[MAXPATHLEN];
    359 	int		longestmatch;
    360 	struct	mnttab	*mountmatch;
    361 
    362 	if (zoneid != GLOBAL_ZONEID) {
    363 		char	*prefix;
    364 
    365 		if ((prefix = getzonerootbyid(zoneid)) == NULL) {
    366 			return (0);
    367 		}
    368 		(void) strncpy(zonepath, prefix, MAXPATHLEN);
    369 		(void) strlcpy(globalpath, prefix, MAXPATHLEN);
    370 		(void) strlcat(globalpath, path, MAXPATHLEN);
    371 		free(prefix);
    372 	} else {
    373 		(void) strlcpy(globalpath, path, MAXPATHLEN);
    374 	}
    375 
    376 	for (;;) {
    377 		longestmatch = 0;
    378 		for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
    379 			struct mnttab *mnt = mlp->mntl_mnt;
    380 			int	len;
    381 
    382 			if (subpath(globalpath, mnt->mnt_mountp) != 0)
    383 				continue;
    384 			len = strlen(mnt->mnt_mountp);
    385 			if (len > longestmatch) {
    386 				mountmatch = mnt;
    387 				longestmatch = len;
    388 			}
    389 		}
    390 		/*
    391 		 * Handle interesting mounts.
    392 		 */
    393 		if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) ||
    394 		    (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) {
    395 			if (zoneid > GLOBAL_ZONEID) {
    396 				struct mnttab *m = NULL;
    397 
    398 				if (strcmp(mountmatch->mnt_fstype,
    399 				    MNTTYPE_AUTOFS) == 0)
    400 					m = mountmatch;
    401 				if (getnfspathbyautofs(mlist, zoneid, m,
    402 				    globalpath, zonepath, MAXPATHLEN) == 0) {
    403 					return (0);
    404 				}
    405 			}
    406 			break;
    407 		} else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) {
    408 			/*
    409 			 * count up what's left
    410 			 */
    411 			int	remainder;
    412 
    413 			remainder = strlen(globalpath) - longestmatch;
    414 			if (remainder > 0) {
    415 				path = pathsuffix(globalpath,
    416 				    mountmatch->mnt_mountp);
    417 				(void) strlcpy(lofspath, path, MAXPATHLEN);
    418 			}
    419 			(void) strlcpy(globalpath, mountmatch->mnt_special,
    420 			    MAXPATHLEN);
    421 			if (remainder > 0) {
    422 				(void) strlcat(globalpath, lofspath,
    423 				    MAXPATHLEN);
    424 			}
    425 		} else {
    426 			if ((zoneid > GLOBAL_ZONEID) &&
    427 			    (strncmp(path, "/home/", strlen("/home/")) == 0)) {
    428 				char zonename[ZONENAME_MAX];
    429 
    430 				/*
    431 				 * If this is a cross-zone reference to
    432 				 * a home directory, it must be corrected.
    433 				 * We should only get here if the zone's
    434 				 * automounter hasn't yet mounted its
    435 				 * autofs trigger on /home.
    436 				 *
    437 				 * Since it is likely to do so in the
    438 				 * future, we will assume that the global
    439 				 * zone already has an equivalent autofs
    440 				 * mount established. By convention,
    441 				 * this should be mounted at the
    442 				 * /zone/<zonename>
    443 				 */
    444 
    445 				if (zone_getattr(zoneid, ZONE_ATTR_NAME,
    446 				    zonename, ZONENAME_MAX) == -1) {
    447 					return (0);
    448 				} else {
    449 					(void) snprintf(globalpath, MAXPATHLEN,
    450 					    "/zone/%s%s", zonename, path);
    451 				}
    452 			}
    453 			break;
    454 		}
    455 	}
    456 	return (1);
    457 }
    458 
    459 
    460 /*
    461  * This function is only useful for global zone callers
    462  * It uses the global zone mnttab to translate local zone pathnames
    463  * into global zone pathnames.
    464  */
    465 char *
    466 getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize,
    467     const bslabel_t *sl)
    468 {
    469 	char		ret_path[MAXPATHLEN];	/* pathname to return */
    470 	zoneid_t	zoneid;
    471 	struct mntlist *mlist;
    472 
    473 	if (getzoneid() != GLOBAL_ZONEID) {
    474 		errno = EINVAL;
    475 		return (NULL);
    476 	}
    477 
    478 	if (path_name[0] != '/') {		/* need absolute pathname */
    479 		errno = EINVAL;
    480 		return (NULL);
    481 	}
    482 
    483 	if (resolved_path == NULL) {
    484 		errno = EINVAL;
    485 		return (NULL);
    486 	}
    487 
    488 	if ((zoneid = getzoneidbylabel(sl)) == -1)
    489 		return (NULL);
    490 
    491 	/*
    492 	 * Construct the list of mounted file systems.
    493 	 */
    494 
    495 	if ((mlist = tsol_mkmntlist()) == NULL) {
    496 		return (NULL);
    497 	}
    498 	if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) {
    499 		tsol_mlist_free(mlist);
    500 		return (NULL);
    501 	}
    502 	tsol_mlist_free(mlist);
    503 	if (strlen(ret_path) >= bufsize) {
    504 		errno = EFAULT;
    505 		return (NULL);
    506 	}
    507 	return (strcpy(resolved_path, ret_path));
    508 } /* end getpathbylabel() */
    509