root/usr/src/lib/libfsmgt/common/fs_mount_defaults.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Traverses /etc/vfstab in order to find default mount information about
 * file systems on the current host.
 */
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/vfstab.h>
#include <sys/types.h>
#include <strings.h>
#include <thread.h>
#include <synch.h>
#include "libfsmgt.h"

/*
 * Private constants
 */

static const char sepstr[] = "\t\n";

/*
 * Private variables
 */
static mutex_t          vfstab_lock = DEFAULTMUTEX;


/*
 * Private method declarations
 */
static int cmp_fields(char *, char *, int);
static fs_mntdefaults_t *create_mntdefaults_entry(struct vfstab vfstab_entry,
                                            int *errp);
static struct vfstab    *create_vfstab_filter(fs_mntdefaults_t *filter,
                                            int *errp);
static void             free_vfstab_entry(struct vfstab *vfstab_entry);
static char             *create_vfstab_entry_line(struct vfstab *, int *);
static int              vfstab_line_cmp(fs_mntdefaults_t *, struct vfstab *);

/*
 * Public methods
 */

void fs_free_mntdefaults_list(fs_mntdefaults_t *headp) {
        fs_mntdefaults_t        *tmp;

        while (headp != NULL) {
                tmp = headp->next;
                free(headp->resource);
                free(headp->fsckdevice);
                free(headp->mountp);
                free(headp->fstype);
                free(headp->fsckpass);
                free(headp->mountatboot);
                free(headp->mntopts);
                headp->next = NULL;
                free(headp);

                headp = tmp;
        }
} /* fs_free_mntdefaults_list */

/*
 * Filter by the fields that are filled in on the filter parameter.
 * Fields that aren't used in filtering the defaults will be NULL.
 */
fs_mntdefaults_t *fs_get_filtered_mount_defaults(fs_mntdefaults_t *filter,
    int *errp) {

        fs_mntdefaults_t        *newp;
        fs_mntdefaults_t        *headp;
        fs_mntdefaults_t        *tailp;
        FILE                    *fp;

        headp = NULL;
        tailp = NULL;


        if ((fp = fopen(VFSTAB, "r")) != NULL) {
                struct vfstab   vfstab_entry;
                struct vfstab   *search_entry;
                (void) mutex_lock(&vfstab_lock);
                search_entry = create_vfstab_filter(filter, errp);
                if (search_entry == NULL) {
                        /*
                         * Out of memory, the error pointer (errp) gets
                         * set in create_vfstab_filter.
                         */
                        fs_free_mntdefaults_list(headp);
                        (void) mutex_unlock(&vfstab_lock);
                        (void) fclose(fp);
                        return (NULL);
                }

                while (getvfsany(fp, &vfstab_entry, search_entry) == 0) {
                        /*
                         * Add to list to be returned
                         */
                        newp = create_mntdefaults_entry(vfstab_entry, errp);
                        if (newp == NULL) {
                                /*
                                 * Out of memory, the error pointer (errp)
                                 * gets set in create_mntdefaults_entry.
                                 */
                                fs_free_mntdefaults_list(headp);
                                (void) mutex_unlock(&vfstab_lock);
                                (void) fclose(fp);
                                return (NULL);
                        }

                        if (headp == NULL) {
                                headp = newp;
                                tailp = newp;
                        } else {
                                tailp->next = newp;
                                tailp = newp;
                        }
                }
                free_vfstab_entry(search_entry);
                (void) mutex_unlock(&vfstab_lock);
                (void) fclose(fp);

        } else {
                *errp = errno;
        } /* if ((fp = fopen(VFSTAB, "r")) != NULL) */

        return (headp);
} /* fs_get_filtered_mount_defaults */


fs_mntdefaults_t *
fs_get_mount_defaults(int *errp)
{
        fs_mntdefaults_t        *newp;
        fs_mntdefaults_t        *headp;
        fs_mntdefaults_t        *tailp;
        FILE                    *fp;

        headp = NULL;
        tailp = NULL;

        if ((fp = fopen(VFSTAB, "r")) != NULL) {
                struct vfstab   vfstab_entry;
                (void) mutex_lock(&vfstab_lock);
                while (getvfsent(fp, &vfstab_entry) == 0) {
                        /*
                         * Add entry to list
                         */
                        newp = create_mntdefaults_entry(vfstab_entry, errp);

                        if (newp == NULL) {
                                /*
                                 * Out of memory, the error pointer (errp)
                                 * gets set in create_mntdefaults_entry.
                                 */
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                fs_free_mntdefaults_list(headp);
                                return (NULL);
                        }

                        if (headp == NULL) {
                                headp = newp;
                                tailp = newp;
                        } else {
                                tailp->next = newp;
                                tailp = newp;
                        }
                }
                (void) fclose(fp);
                (void) mutex_unlock(&vfstab_lock);
        } else {
                *errp = errno;
        } /* if ((fp = fopen(VFSTAB, "r")) != NULL) */

        /*
         * Caller must free the returned list
         */
        return (headp);

} /* fs_get_mount_defaults */

fs_mntdefaults_t *
fs_add_mount_default(fs_mntdefaults_t *newp, int *errp) {

        FILE *fp;
        struct vfstab *new_entry;
        fs_mntdefaults_t *ret_val;

        new_entry = create_vfstab_filter(newp, errp);
        if (new_entry != NULL) {
                if ((fp = fopen(VFSTAB, "a")) != NULL) {
                        (void) mutex_lock(&vfstab_lock);
                        putvfsent(fp, new_entry);
                        free_vfstab_entry(new_entry);
                        (void) fclose(fp);
                        (void) mutex_unlock(&vfstab_lock);
                        ret_val = fs_get_mount_defaults(errp);
                } else {
                        *errp = errno;
                        free_vfstab_entry(new_entry);
                        ret_val = NULL;
                }
        } else {
                ret_val = NULL;
        }
        return (ret_val);
} /* fs_add_mount_default */


fs_mntdefaults_t *
fs_edit_mount_defaults(
    fs_mntdefaults_t *old_vfstab_ent,
    fs_mntdefaults_t *new_vfstab_ent,
    int *errp)
{
        FILE *fp;
        fs_mntdefaults_t *ret_val;
        char vfstab_line[VFS_LINE_MAX];
        char **temp_vfstab = NULL;
        char *new_line;
        struct vfstab vfstabp, *new_vfstab;
        int line_found = 0;

        if ((fp = fopen(VFSTAB, "r")) != NULL) {
                char *tmp;
                int count = 0;
                (void) mutex_lock(&vfstab_lock);
                while (fgets(vfstab_line, VFS_LINE_MAX, fp) != NULL) {
                        char *charp;
                        struct vfstab *vp;
                        char *orig_line = strdup(vfstab_line);
                        if (orig_line == NULL) {
                                *errp = ENOMEM;
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                return (NULL);
                        }
                        vp = &vfstabp;
                        for (charp = vfstab_line;
                            *charp == ' ' || *charp == '\t'; charp++);
                        if (*charp == '#' || *charp == '\n') {
                                /*
                                 * Write comments out to temp vfstab
                                 * image
                                 */
                                if (!fileutil_add_string_to_array(
                                    &temp_vfstab, vfstab_line, &count, errp)) {
                                    ret_val = NULL;
                                    line_found = 0;
                                        break;
                                }
                                continue;
                        }
                        vp->vfs_special = (char *)strtok_r(
                            vfstab_line, sepstr, &tmp);
                        vp->vfs_fsckdev = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_mountp = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_fstype = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_fsckpass = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_automnt = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_mntopts = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        if (strtok_r(NULL, sepstr, &tmp) != NULL) {
                                /*
                                 * Invalid vfstab line.
                                 */
                                *errp = EINVAL;
                                (void) mutex_unlock(&vfstab_lock);
                                (void) fclose(fp);
                                return (NULL);
                        }

                        if (vfstab_line_cmp(old_vfstab_ent, vp)) {
                                line_found = 1;
                                new_vfstab =
                                    create_vfstab_filter(
                                    new_vfstab_ent, errp);
                                new_line =
                                    create_vfstab_entry_line(new_vfstab, errp);
                                if (!fileutil_add_string_to_array(
                                    &temp_vfstab, new_line, &count, errp)) {
                                        ret_val = NULL;
                                        line_found = 0;
                                        free(new_line);
                                        break;
                                }
                                free(new_line);
                        } else {
                                if (!fileutil_add_string_to_array(
                                    &temp_vfstab, orig_line, &count, errp)) {
                                        ret_val = NULL;
                                        line_found = 0;
                                        break;
                                }
                        }
                        free(orig_line);
                }
                (void) fclose(fp);

                if (line_found && temp_vfstab != NULL) {
                        if ((fp = fopen(VFSTAB, "w")) != NULL) {
                                int i;
                                for (i = 0; i < count; i++) {
                                        fprintf(fp, "%s", temp_vfstab[i]);
                                }
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                ret_val = fs_get_mount_defaults(errp);
                                fileutil_free_string_array(temp_vfstab, count);
                        } else {
                                *errp = errno;
                                (void) mutex_unlock(&vfstab_lock);
                                ret_val = NULL;
                        }
                } else {
                        *errp = errno;
                        (void) mutex_unlock(&vfstab_lock);
                        ret_val = NULL;
                }
        } else {
                *errp = errno;
                ret_val = NULL;
        }
        return (ret_val);
} /* fs_edit_mount_defaults */

fs_mntdefaults_t *
fs_del_mount_default_ent(fs_mntdefaults_t *old_vfstab_ent, int *errp)
{
        FILE *fp;
        fs_mntdefaults_t *ret_val;
        char vfstab_line[VFS_LINE_MAX];
        struct vfstab vfstabp;
        int line_found = 0;

        if ((fp = fopen(VFSTAB, "r")) != NULL) {
                struct vfstab *vp;
                char *tmp;
                char *charp;
                char *orig_line = NULL;
                char **temp_vfstab = NULL;
                int count = 0;
                vp = &vfstabp;
                (void) mutex_lock(&vfstab_lock);
                while (fgets(vfstab_line, VFS_LINE_MAX, fp) != NULL) {

                        orig_line = strdup(vfstab_line);
                        if (orig_line == NULL) {
                                *errp = ENOMEM;
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                return (NULL);
                        }

                        for (charp = vfstab_line;
                            *charp == ' ' || *charp == '\t'; charp++);

                        if (*charp == '#' || *charp == '\n') {
                                /*
                                 * Write comments out to temp vfstab
                                 * image
                                 */
                                if (!fileutil_add_string_to_array(
                                    &temp_vfstab, vfstab_line, &count, errp)) {
                                        ret_val = NULL;
                                        line_found = 0;
                                        free(orig_line);
                                        break;
                                }
                                continue;
                        }

                        vp->vfs_special = (char *)strtok_r(
                            vfstab_line, sepstr, &tmp);
                        vp->vfs_fsckdev = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_mountp = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_fstype = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_fsckpass = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_automnt = (char *)strtok_r(
                            NULL, sepstr, &tmp);
                        vp->vfs_mntopts = (char *)strtok_r(
                            NULL, sepstr, &tmp);

                        if (strtok_r(NULL, sepstr, &tmp) != NULL) {
                                /*
                                 * Invalid vfstab line.
                                 */
                                *errp = EINVAL;
                                free(orig_line);
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                return (NULL);
                        }

                        if (vfstab_line_cmp(old_vfstab_ent, vp)) {
                                line_found = 1;
                        } else {
                                if (!fileutil_add_string_to_array(
                                    &temp_vfstab, orig_line, &count, errp)) {
                                        ret_val = NULL;
                                        line_found = 0;
                                        free(orig_line);
                                        break;
                                }
                        }
                        free(orig_line);
                }

                (void) fclose(fp);

                if (line_found && temp_vfstab != NULL) {
                        if ((fp = fopen(VFSTAB, "w")) != NULL) {
                                int i;
                                for (i = 0; i < count; i++) {
                                        fprintf(fp, "%s", temp_vfstab[i]);
                                }
                                (void) fclose(fp);
                                (void) mutex_unlock(&vfstab_lock);
                                ret_val = fs_get_mount_defaults(errp);
                                fileutil_free_string_array(temp_vfstab, count);
                        } else {
                                *errp = errno;
                                (void) mutex_unlock(&vfstab_lock);
                                fileutil_free_string_array(temp_vfstab, count);
                                ret_val = NULL;
                        }
                } else {
                        (void) mutex_unlock(&vfstab_lock);
                        if (temp_vfstab != NULL) {
                                fileutil_free_string_array(temp_vfstab, count);
                        }
                        ret_val = NULL;
                }
        } else {
                *errp = errno;
                ret_val = NULL;
        }
        return (ret_val);
}

/*
 * Private methods
 */

static fs_mntdefaults_t *
create_mntdefaults_entry(struct vfstab vfstab_entry, int *errp) {
        fs_mntdefaults_t        *newp;

        newp = (fs_mntdefaults_t *)calloc((size_t)1,
            (size_t)sizeof (fs_mntdefaults_t));

        if (newp == NULL) {
                /*
                 * Out of memory
                 */
                *errp = errno;
                return (NULL);
        }


        if (vfstab_entry.vfs_special != NULL) {
                newp->resource = strdup(vfstab_entry.vfs_special);
                if (newp->resource == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }


        if (vfstab_entry.vfs_fsckdev != NULL) {
                newp->fsckdevice = strdup(vfstab_entry.vfs_fsckdev);
                if (newp->fsckdevice == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }

        if (vfstab_entry.vfs_mountp != NULL) {
                newp->mountp = strdup(vfstab_entry.vfs_mountp);
                if (newp->mountp == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }

        if (vfstab_entry.vfs_fstype != NULL) {
                newp->fstype = strdup(vfstab_entry.vfs_fstype);
                if (newp->fstype == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }

        if (vfstab_entry.vfs_fsckpass != NULL) {
                newp->fsckpass = strdup(vfstab_entry.vfs_fsckpass);
                if (newp->fsckpass == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }

        if (vfstab_entry.vfs_automnt != NULL) {
                newp->mountatboot = strdup(vfstab_entry.vfs_automnt);
                if (newp->mountatboot == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }

        if (vfstab_entry.vfs_mntopts != NULL) {
                newp->mntopts = strdup(vfstab_entry.vfs_mntopts);
                if (newp->mntopts == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        fs_free_mntdefaults_list(newp);
                        return (NULL);
                }
        }
        newp->next = NULL;

        return (newp);

} /* create_mntdefaults_entry */

static struct vfstab *
create_vfstab_filter(fs_mntdefaults_t *filter, int *errp) {
        struct vfstab *search_entry;

        search_entry = (struct vfstab *)calloc((size_t)1,
            (size_t)sizeof (struct vfstab));
        if (search_entry == NULL) {
                /*
                 * Out of memory
                 */
                *errp = errno;
                return (NULL);
        }

        /*
         * Populate the filter criteria
         */
        if (filter->resource != NULL) {
                search_entry->vfs_special = strdup(filter->resource);
                if (search_entry->vfs_special == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }

        }

        if (filter->fsckdevice != NULL) {
                search_entry->vfs_fsckdev = strdup(filter->fsckdevice);
                if (search_entry->vfs_fsckdev ==  NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        if (filter->mountp != NULL) {
                search_entry->vfs_mountp = strdup(filter->mountp);
                if (search_entry->vfs_mountp == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        if (filter->fstype != NULL) {
                search_entry->vfs_fstype = strdup(filter->fstype);
                if (search_entry->vfs_fstype == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        if (filter->fsckpass != NULL) {
                search_entry->vfs_fsckpass = strdup(filter->fsckpass);
                if (search_entry->vfs_fsckpass == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        if (filter->mountatboot != NULL) {
                search_entry->vfs_automnt = strdup(filter->mountatboot);
                if (search_entry->vfs_automnt == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        if (filter->mntopts != NULL) {
                search_entry->vfs_mntopts = strdup(filter->mntopts);
                if (search_entry->vfs_mntopts == NULL) {
                        /*
                         * Out of memory
                         */
                        *errp = errno;
                        free_vfstab_entry(search_entry);
                        return (NULL);
                }
        }

        return (search_entry);
} /* create_vfstab_filter */

static void free_vfstab_entry(struct vfstab *vfstab_entry) {

        free(vfstab_entry->vfs_special);
        free(vfstab_entry->vfs_fsckdev);
        free(vfstab_entry->vfs_mountp);
        free(vfstab_entry->vfs_fstype);
        free(vfstab_entry->vfs_fsckpass);
        free(vfstab_entry->vfs_automnt);
        free(vfstab_entry->vfs_mntopts);

        free(vfstab_entry);
} /* free_vfstab_entry */

static int
vfstab_line_cmp(fs_mntdefaults_t *mntdftp, struct vfstab *vp) {

        int ret_val = 1;

        ret_val = cmp_fields(mntdftp->resource, vp->vfs_special, ret_val);
        ret_val = cmp_fields(mntdftp->mountp, vp->vfs_mountp, ret_val);

        return (ret_val);
} /* vfstab_line_cmp */

/*
 * Helper function for comparing fields in a fs_mntdefaults_t to a
 * vfstab structure. Used in vfstab_line_cmp().
 */
static int
cmp_fields(char *mntdflt_str, char *vfstab_str, int ret_val) {
        if (ret_val != 0) {
                if (mntdflt_str != NULL && vfstab_str != NULL) {
                        if (strcmp(mntdflt_str, vfstab_str) != 0) {
                                ret_val = 0;
                        }
                } else if (mntdflt_str == NULL || vfstab_str == NULL) {
                        ret_val = 0;
                }
        }
        return (ret_val);
} /* cmp_fields */

/*
 * Helper fuction used by del_vfstab_ent() and edit_vfstab_ent() to
 * create a vfstab line for writing out to the vfstab file.
 */
char *
create_vfstab_entry_line(struct vfstab *vfstab_ent, int *errp) {
        char *line;
        int line_length;
        line_length = (
            (vfstab_ent->vfs_special ?
                (strlen(vfstab_ent->vfs_special) +1) : 2) +
            (vfstab_ent->vfs_fsckdev ?
                (strlen(vfstab_ent->vfs_fsckdev) +1) : 2) +
            (vfstab_ent->vfs_mountp ?
                (strlen(vfstab_ent->vfs_mountp) +1) : 2) +
            (vfstab_ent->vfs_fstype ?
                (strlen(vfstab_ent->vfs_fstype) +1) : 2) +
            (vfstab_ent->vfs_fsckpass ?
                (strlen(vfstab_ent->vfs_fsckpass) +1) : 2) +
            (vfstab_ent->vfs_automnt ?
                (strlen(vfstab_ent->vfs_automnt) +1) : 2) +
            (vfstab_ent->vfs_mntopts ?
                (strlen(vfstab_ent->vfs_mntopts) +1) : 2));
        line = (char *)malloc(line_length + 1);
        if (line != NULL) {
                sprintf(line, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
                    vfstab_ent->vfs_special ? vfstab_ent->vfs_special : "-",
                    vfstab_ent->vfs_fsckdev ? vfstab_ent->vfs_fsckdev : "-",
                    vfstab_ent->vfs_mountp ? vfstab_ent->vfs_mountp : "-",
                    vfstab_ent->vfs_fstype ? vfstab_ent->vfs_fstype : "-",
                    vfstab_ent->vfs_fsckpass ? vfstab_ent->vfs_fsckpass : "-",
                    vfstab_ent->vfs_automnt ? vfstab_ent->vfs_automnt : "-",
                    vfstab_ent->vfs_mntopts ? vfstab_ent->vfs_mntopts : "-");
        } else {
                *errp = errno;
        }
        return (line);
} /* create_vfstab_entry_line */