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