root/usr/src/lib/libinstzones/common/zones_lofs.c
/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libintl.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <sys/param.h>
#include <libzonecfg.h>
#include "zones_strings.h"
#include "instzones_lib.h"

#define MNTTAB  "/etc/mnttab"

#define MNTTAB_HUNK     32

static struct mnttab *mountTable;
static size_t mountTableSize = 0;
static boolean_t createdFlag = B_FALSE;

/*
 * Name         : z_createMountTable
 * Description  : Populate the mountTable Array with mnttab entries
 * Arguments    : void
 * Returns      : int
 *                0: The Mount Table was succesfully initialized
 *               -1: There was an error during initialisation
 */
int
z_createMountTable(void)
{
        FILE *fp;
        struct mnttab ent;
        struct mnttab *entp;

        if (createdFlag) {
                return (0);
        }

        fp = fopen(MNTTAB, "r");
        if (fp == NULL) {
                _z_program_error(ERR_OPEN_READ, MNTTAB, errno,
                    strerror(errno));
                return (-1);
        }

        /* Put the entries into the table */
        mountTable = NULL;
        mountTableSize = 0;
        createdFlag = B_TRUE;
        while (getmntent(fp, &ent) == 0) {
                if (mountTableSize % MNTTAB_HUNK == 0) {
                        mountTable = _z_realloc(mountTable,
                            (mountTableSize + MNTTAB_HUNK) * sizeof (ent));
                }
                entp = &mountTable[mountTableSize++];

                /*
                 * Zero out any fields we're not using.
                 */
                (void) memset(entp, 0, sizeof (*entp));

                if (ent.mnt_special != NULL)
                        entp->mnt_special = _z_strdup(ent.mnt_special);
                if (ent.mnt_mntopts != NULL)
                        entp->mnt_mntopts = _z_strdup(ent.mnt_mntopts);
                entp->mnt_mountp = _z_strdup(ent.mnt_mountp);
                entp->mnt_fstype = _z_strdup(ent.mnt_fstype);
        }

        (void) fclose(fp);
        return (0);
}

/*
 * Name         : findPathRWStatus
 * Description  : Check whether the given path is an mnttab entry
 * Arguments    : char * - The Path to be verified
 * Returns      : int
 *                -1: The Path is NOT present in the table (mnttab)
 *                 0: The Path is present in the table and is mounted read-only
 *                 1: The Path is present in the table and is mounted read-write
 */
static int
findPathRWStatus(const char *a_path)
{
        int i;

        for (i = 0; i < mountTableSize; i++) {
                if (strcmp(a_path, mountTable[i].mnt_mountp) == 0) {
                        if (hasmntopt(&mountTable[i], MNTOPT_RO) != NULL) {
                                return (0);
                        } else {
                                return (1);
                        }
                }
        }

        return (-1);
}


/*
 * Name         : z_isPathWritable
 * Description  : Check if the given path is in a writable area
 * Arguments    : char * - The Path to be verified
 * Returns      : int
 *                 0: The Path is under a read-only mount
 *                 1: The Path is under a read-write mount
 * NOTE         : This funcion automatically initialises
 *                the mountPoint table if needed.
 */
int
z_isPathWritable(const char *a_str)
{
        int i, result, slen;
        char a_path[MAXPATHLEN];

        if (!createdFlag) {
                if (z_createMountTable() != 0) {
                        return (1);
                }
        }

        (void) strlcpy(a_path, a_str, sizeof (a_path));
        slen = strlen(a_path);

        /*
         * This for loop traverses Path backwards, incrementally removing the
         * basename of Path and looking for the resultant directory in the
         * mnttab.  Once found, it returns the rw status of that file system.
         */
        for (i = slen; i > 0; i--) {
                if ((a_path[i] == '/') || (a_path[i] == '\0')) {
                        a_path[i] = '\0';
                        result = findPathRWStatus(a_path);
                        if (result != -1) {
                                return (result);
                        }
                }
        }

        return (1);
}

/*
 * Name         : z_destroyMountTable
 * Description  : Clear the entries in the mount table
 * Arguments    : void
 * Returns      : void
 */
void
z_destroyMountTable(void)
{
        int i;

        if (!createdFlag) {
                return;
        }

        if (mountTable == NULL) {
                return;
        }

        for (i = 0; i < mountTableSize; i++) {
                free(mountTable[i].mnt_mountp);
                free(mountTable[i].mnt_fstype);
                free(mountTable[i].mnt_special);
                free(mountTable[i].mnt_mntopts);
                assert(mountTable[i].mnt_time == NULL);
        }

        free(mountTable);
        mountTable = NULL;
        mountTableSize = 0;
        createdFlag = B_FALSE;
}

/*
 * Name         : z_resolve_lofs
 * Description  : Loop over potential loopback mounts and symlinks in a
 *                given path and resolve them all down to an absolute path.
 * Arguments    : char * - path to resolve.  path is in writable storage.
 *                size_t - length of path storage.
 * Returns      : void
 */
void
z_resolve_lofs(char *path, size_t pathlen)
{
        int len, arlen, i;
        const char *altroot;
        char tmppath[MAXPATHLEN];
        boolean_t outside_altroot;

        if ((len = resolvepath(path, tmppath, sizeof (tmppath))) == -1)
                return;

        tmppath[len] = '\0';
        (void) strlcpy(path, tmppath, pathlen);

        if (z_createMountTable() == -1)
                return;

        altroot = zonecfg_get_root();
        arlen = strlen(altroot);
        outside_altroot = B_FALSE;
        for (;;) {
                struct mnttab *mnp;

                /* Search in reverse order to find longest match */
                for (i = mountTableSize; i > 0; i--) {
                        mnp = &mountTable[i - 1];
                        if (mnp->mnt_fstype == NULL ||
                            mnp->mnt_mountp == NULL ||
                            mnp->mnt_special == NULL)
                                continue;
                        len = strlen(mnp->mnt_mountp);
                        if (strncmp(mnp->mnt_mountp, path, len) == 0 &&
                            (path[len] == '/' || path[len] == '\0'))
                                break;
                }
                if (i <= 0)
                        break;

                /* If it's not a lofs then we're done */
                if (strcmp(mnp->mnt_fstype, MNTTYPE_LOFS) != 0)
                        break;

                if (outside_altroot) {
                        char *cp;
                        int olen = sizeof (MNTOPT_RO) - 1;

                        /*
                         * If we run into a read-only mount outside of the
                         * alternate root environment, then the user doesn't
                         * want this path to be made read-write.
                         */
                        if (mnp->mnt_mntopts != NULL &&
                            (cp = strstr(mnp->mnt_mntopts, MNTOPT_RO)) !=
                            NULL &&
                            (cp == mnp->mnt_mntopts || cp[-1] == ',') &&
                            (cp[olen] == '\0' || cp[olen] == ',')) {
                                break;
                        }
                } else if (arlen > 0 &&
                    (strncmp(mnp->mnt_special, altroot, arlen) != 0 ||
                    (mnp->mnt_special[arlen] != '\0' &&
                    mnp->mnt_special[arlen] != '/'))) {
                        outside_altroot = B_TRUE;
                }
                /* use temporary buffer because new path might be longer */
                (void) snprintf(tmppath, sizeof (tmppath), "%s%s",
                    mnp->mnt_special, path + len);
                if ((len = resolvepath(tmppath, path, pathlen)) == -1)
                        break;
                path[len] = '\0';
        }
}