root/usr/src/lib/libfsmgt/common/fs_dfstab.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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <thread.h>
#include <synch.h>
#include "libfsmgt.h"

/*
 * Private datastructures.
 */
typedef struct dfstab_entry {
        struct dfstab_entry *next;
        char    *path;
        char    *resource;
        char    *fstype;
        char    *options;
        char    *description;
} dfstab_entry_t;

static const char *whitespace = " \t";
static mutex_t dfstab_lock = DEFAULTMUTEX;

/*
 * Private functions
 */
static dfstab_entry_t *get_dfstab_ents(int *);
static void free_dfstab_list(dfstab_entry_t *);
static dfstab_entry_t *dfstab_line_to_dfstab_entry(char *, int *);
static char *create_share_cmd(dfstab_entry_t *, char *, int *);
static dfstab_entry_t *change_dfstab_ent(dfstab_entry_t *,
        dfstab_entry_t *, int *);
static void add_entry_to_dfstab(dfstab_entry_t *, int *);


static dfstab_entry_t *
get_dfstab_ents(int *err)
{
        dfstab_entry_t *dfstablist, *headptr, *tailptr = NULL;
        FILE *dfp;              /* fp for dfs list */
        static char cmd[BUFSIZE];
        *err = 0;

        if ((dfp = fopen(DFSTAB, "r")) != NULL) {
                char *share_cmd;
                (void) mutex_lock(&dfstab_lock);
                while ((share_cmd =
                    fileutil_getline(dfp, cmd, BUFSIZE)) != NULL) {
                        if ((dfstablist =
                            dfstab_line_to_dfstab_entry(share_cmd, err)) !=
                            NULL) {
                                if (tailptr == NULL) {
                                        headptr = dfstablist;
                                        tailptr = dfstablist;
                                } else {
                                        tailptr->next = dfstablist;
                                        tailptr = dfstablist;
                                }
                                dfstablist = dfstablist->next;
                        } else {
                                free(share_cmd);
                                break;
                        }
                        free(share_cmd);
                }
                if (tailptr == NULL) {
                        headptr = tailptr;
                }
                (void) mutex_unlock(&dfstab_lock);
                fclose(dfp);
        } else {
                *err = errno;
                (void) fprintf(stderr, "%s: cannot open %s\n", cmd, DFSTAB);
                headptr = NULL;
        }
        return (headptr);
} /* get_dfstab_ents */

static void
add_entry_to_dfstab(dfstab_entry_t *list, int *err)
{
        FILE *dfp;              /* fp for dfs list */

        if ((dfp = fopen(DFSTAB, "a")) != NULL) {
                char *share_cmd;
                if ((share_cmd = create_share_cmd(list, NULL, err)) != NULL) {
                        (void) mutex_lock(&dfstab_lock);
                        fprintf(dfp, "%s", share_cmd);
                        fclose(dfp);
                        (void) mutex_unlock(&dfstab_lock);
                        free(share_cmd);
                } else {
                        *err = errno;
                }
        } else {
                *err = errno;
        }

} /* add_entry_to_dfstab */

static void
free_dfstab_list(dfstab_entry_t *headp)
{
        dfstab_entry_t *tmp = headp;

        while (headp != NULL) {
                tmp = headp->next;
                if (headp->path != NULL) {
                        free(headp->path);
                }
                if (headp->resource != NULL) {
                        free(headp->resource);
                }
                if (headp->fstype != NULL) {
                        free(headp->fstype);
                }
                if (headp->options != NULL) {
                        free(headp->options);
                }
                if (headp->description != NULL) {
                        free(headp->description);
                }
                headp->next = NULL;
                free(headp);
                headp = tmp;
        }
} /* free_dfstab_list */

static char *
create_share_cmd(dfstab_entry_t *new_entry, char *temp_line, int *err)
{
        char tempstr[BUFSIZE];
        char *cmd, *ret_val;

        cmd = (char *)calloc((size_t)1, BUFSIZE);
        if (cmd == NULL) {
                *err = errno;
                return (NULL);
        }
        sprintf(cmd, "share ");
        if (new_entry->fstype) {
                sprintf(tempstr, "-F %s ", new_entry->fstype);
                strlcat(cmd, tempstr, BUFSIZE);
        }
        if (new_entry->options) {
                sprintf(tempstr, "-o %s ", new_entry->options);
                strlcat(cmd, tempstr, BUFSIZE);
        }
        if (new_entry->description) {
                sprintf(tempstr, "-d %s ",
                    new_entry->description);
                strlcat(cmd, tempstr, BUFSIZE);
        }
        sprintf(tempstr, "%s\n", new_entry->path);
        strlcat(cmd, tempstr, BUFSIZE);
        if (temp_line != NULL && strchr(temp_line, '#')) {
                sprintf(tempstr, " %s", strchr(temp_line, '#'));
                strlcat(cmd, tempstr, BUFSIZE);
        }
        ret_val = strdup(cmd);
        free(cmd);
        return (ret_val);
} /* create_share_cmd */

/*
 * dfstab_line_to_dfstab_entry - parses a line from dfstab and fills in
 * the fields of a dfstab_entry_t structure
 * Parameters:
 * char *cmd - the share command or dfstab line to be parsed
 * int *err - a pointer for returning any error codes encountered
 */
static dfstab_entry_t *
dfstab_line_to_dfstab_entry(char *cmd, int *err)
{

        dfstab_entry_t *dfstablist;
        extern char *optarg;
        extern int optind;
        int c, argcount = 0;
        char *temp_str;
        char *arglist[LINESZ];

        c = 0;
        optind = 1;

        temp_str = strdup(cmd);
        if (temp_str == NULL) {
                *err = ENOMEM;
                return (NULL);
        }

        for (arglist[argcount] = strtok(temp_str, whitespace);
            arglist[argcount] != NULL; /* CSTYLED */) {
                arglist[++argcount] = strtok(NULL, whitespace);
        }
        argcount--;
        dfstablist =
            (dfstab_entry_t *)calloc((size_t)1,
            sizeof (dfstab_entry_t));
        if (dfstablist == NULL) {
                *err = ENOMEM;
                free(temp_str);
                return (NULL);
        }
        while ((c = getopt(argcount, arglist, "F:d:o:")) != -1) {
                switch (c) {
                case 'F':
                                        /* file system type */
                                        /* at most one -F */
                        *err |= (dfstablist->fstype != NULL);
                        dfstablist->fstype = strdup(optarg);
                        if (dfstablist->fstype == NULL) {
                                *err = ENOMEM;
                                free_dfstab_list(dfstablist);
                                free(temp_str);
                                return (NULL);
                        }
                        break;
                case 'd':               /* description */
                                        /* at most one -d */
                        *err |= (dfstablist->description != NULL);
                        dfstablist->description = strdup(optarg);
                        if (dfstablist->description == NULL) {
                                *err = ENOMEM;
                                free_dfstab_list(dfstablist);
                                free(temp_str);
                                return (NULL);
                        }
                        break;
                case 'o':               /* fs specific options */
                                        /* at most one - o */
                        *err |= (dfstablist->options != NULL);
                        dfstablist->options = strdup(optarg);
                        if (dfstablist->options == NULL) {
                                *err = ENOMEM;
                                free_dfstab_list(dfstablist);
                                free(temp_str);
                                return (NULL);
                        }
                        break;
                case '?':
                        *err = 1;
                        break;
                }
        }
        if (dfstablist->fstype == NULL) {
                FILE *fp;

                if ((fp = fopen(DFSTYPES, "r")) == NULL) {
                        (void) fprintf(stderr, "%s: cannot open %s\n",
                            cmd, DFSTYPES);
                        free_dfstab_list(dfstablist);
                        free(temp_str);
                        return (NULL);
                }
                (void) mutex_lock(&dfstab_lock);
                dfstablist->fstype = strdup(fileutil_getfs(fp));
                (void) mutex_unlock(&dfstab_lock);
                fclose(fp);
        }
        dfstablist->path = strdup(arglist[argcount]);
        if (dfstablist->path == NULL) {
                *err = ENOMEM;
                free_dfstab_list(dfstablist);
                free(temp_str);
                return (NULL);
        }
        free(temp_str);
        return (dfstablist);
} /* dfstab_line_to_dfstab_entry */

static dfstab_entry_t *
change_dfstab_ent(
        dfstab_entry_t *old_entry,
        dfstab_entry_t *new_entry,
        int *err)
{

        FILE *fp;
        dfstab_entry_t *temp_list, *ret_val;
        char cmd[BUFSIZE];
        char **temp_dfstab = NULL;
        int line_found = 0;

        if ((fp = fopen(DFSTAB, "r")) != NULL) {
                char *share_cmd;
                int count = 0;
                (void) mutex_lock(&dfstab_lock);
                while (fgets(cmd, BUFSIZE, fp) != NULL) {
                        if ((share_cmd =
                            fileutil_get_cmd_from_string(cmd)) == NULL) {
                                if (!fileutil_add_string_to_array(
                                    &temp_dfstab, cmd, &count, err)) {
                                        ret_val = NULL;
                                        line_found = 0;
                                        break;
                                }
                                continue;
                        }
                        if ((temp_list =
                            dfstab_line_to_dfstab_entry(share_cmd, err)) ==
                            NULL) {
                                free(share_cmd);
                                ret_val = NULL;
                                break;
                        }
                        if (strcmp(old_entry->path,
                            temp_list->path) == 0) {
                                char *new_cmd = NULL;
                                line_found = 1;
                                if (new_entry != NULL && (new_cmd =
                                    create_share_cmd(new_entry, cmd,
                                    err)) != NULL) {
                                        if (!fileutil_add_string_to_array(
                                            &temp_dfstab, new_cmd, &count,
                                            err)) {
                                                ret_val = NULL;
                                                line_found = 0;
                                                free(share_cmd);
                                                free(new_cmd);
                                                break;
                                        }
                                        free(new_cmd);
                                }
                        } else {
                                if (!fileutil_add_string_to_array(
                                    &temp_dfstab, cmd, &count, err)) {
                                        free(share_cmd);
                                        ret_val = NULL;
                                        line_found = 0;
                                        break;
                                }
                        }
                        free_dfstab_list(temp_list);
                        free(share_cmd);
                }
                fclose(fp);

                if (line_found && temp_dfstab != NULL) {
                        if ((fp = fopen(DFSTAB, "w")) != NULL) {
                                int i;
                                for (i = 0; i < count; i++) {
                                        fprintf(fp, "%s", temp_dfstab[i]);
                                }
                                fclose(fp);
                                (void) mutex_unlock(&dfstab_lock);
                                ret_val = get_dfstab_ents(err);
                                fileutil_free_string_array(temp_dfstab, count);
                        } else {
                                *err = errno;
                                (void) mutex_unlock(&dfstab_lock);
                                fileutil_free_string_array(temp_dfstab, count);
                                ret_val = NULL;
                        }
                } else {
                        (void) mutex_unlock(&dfstab_lock);
                        if (temp_dfstab != NULL) {
                                fileutil_free_string_array(temp_dfstab, count);
                        }
                        ret_val = NULL;
                }
        } else {
                *err = errno;
                ret_val = NULL;
        }
        return (ret_val);
} /* change_dfstab_ent */

/*
 * Public accessor functions.
 */

/*
 * fs_add_DFStab_ent - adds an entry to dfstab and to the list of dfstab
 * entries. Returns a pointer to the head of the dfstab entry list.
 * Parameters:
 * char *cmd - the same command to be added to dstab
 * int *err - an error pointer for retruning any errors
 */
fs_dfstab_entry_t
fs_add_DFStab_ent(char *cmd, int *err)
{
        dfstab_entry_t *dfstab_ent;

        dfstab_ent = dfstab_line_to_dfstab_entry(cmd, err);
        if (dfstab_ent == NULL) {
                *err = errno;
                return (NULL);
        }
        add_entry_to_dfstab(dfstab_ent, err);
        if (*err != 0) {
                free_dfstab_list(dfstab_ent);
                return (NULL);
        }
        free_dfstab_list(dfstab_ent);
        return (get_dfstab_ents(err));
}

/*
 * set_DFStab_ent - adds an entry to dfstab and to the list of dfstab entries.
 * returns a pointer to the head of the dfstab entry list.
 */
fs_dfstab_entry_t
fs_set_DFStab_ent(
        char *path,
        char *fstype,
        char *options,
        char *description,
        int *err)
{

        dfstab_entry_t *new_entry;
        new_entry = (dfstab_entry_t *)calloc((size_t)1,
            sizeof (dfstab_entry_t));
        if (new_entry == NULL) {
                *err = ENOMEM;
                return (NULL);
        }
        if (path != NULL) {
                new_entry->path = strdup(path);
        } else {
                *err = EINVAL;
                free_dfstab_list(new_entry);
                return (NULL);
        }
        if (fstype != NULL) {
                new_entry->fstype = strdup(fstype);
        } else {
                FILE *fp;

                if ((fp = fopen(DFSTYPES, "r")) == NULL) {
                        /* change this to error handler */
                        (void) fprintf(stderr, "cannot open %s\n",
                            DFSTYPES);
                        free_dfstab_list(new_entry);
                        return (NULL);
                }
                (void) mutex_lock(&dfstab_lock);
                new_entry->fstype = strdup(fileutil_getfs(fp));
                (void) mutex_unlock(&dfstab_lock);
                fclose(fp);
        }
        if (options != NULL) {
                new_entry->options = strdup(options);
        }
        if (description != NULL) {
                new_entry->description = strdup(description);
        }
        add_entry_to_dfstab(new_entry, err);
        if (*err != 0) {
                free_dfstab_list(new_entry);
                return (NULL);
        }
        free_dfstab_list(new_entry);
        return (get_dfstab_ents(err));
} /* set_DFStab_ent */

/*
 * Accessor function for path element of dfstab entry.
 */
char *
fs_get_DFStab_ent_Path(void *entry)
{
        dfstab_entry_t *entryptr = (dfstab_entry_t *)entry;
        if (entryptr == NULL) {
                return (NULL);
        }
        return (entryptr->path);
} /* get_DFStab_ent_Path */

/*
 * Accessor function for fstype element of dfstab entry.
 */
char *
fs_get_DFStab_ent_Fstype(void *entry)
{
        dfstab_entry_t *entryptr = (dfstab_entry_t *)entry;
        if (entryptr == NULL) {
                return (NULL);
        }
        return (entryptr->fstype);
}

/*
 * Accessor function for options element of dfstab entry.
 */
char *
fs_get_DFStab_ent_Options(void *entry)
{
        dfstab_entry_t *entryptr = (dfstab_entry_t *)entry;
        if (entryptr == NULL) {
                return (NULL);
        }
        return (entryptr->options);
}

/*
 * Accessor function for description element of dfstab entry.
 */
char *
fs_get_DFStab_ent_Desc(void *entry)
{
        dfstab_entry_t *entryptr = (dfstab_entry_t *)entry;
        if (entryptr == NULL) {
                return (NULL);
        }
        return (entryptr->description);
}

/*
 * Accessor function for resource element of dfstab entry.
 */
char *
fs_get_DFStab_ent_Res(void *entry)
{
        dfstab_entry_t *entryptr = (dfstab_entry_t *)entry;
        if (entryptr == NULL) {
                return (NULL);
        }
        return (entryptr->resource);
}


/*
 * Calls get_dfstab_ents to create the list of dfstab
 * entries and returns that list.
 */
fs_dfstab_entry_t
fs_get_DFStab_ents(int *err)
{
        dfstab_entry_t *list;
        list = get_dfstab_ents(err);
        return (list);
}

/*
 * Retrives and returns the next entry in the list.
 */
fs_dfstab_entry_t
fs_get_DFStab_ent_Next(void *list)
{
        dfstab_entry_t *listptr = (dfstab_entry_t *)list;
        if (listptr == NULL) {
                return (NULL);
        }
        return (listptr->next);
}

/*
 * Retrives and returns a share command based on the dfstab entry passed in.
 */
char *
fs_get_Dfstab_share_cmd(fs_dfstab_entry_t dfstab_ent, int *err)
{
        char *share_cmd;
        if (dfstab_ent == NULL) {
                return (NULL);
        }
        share_cmd = create_share_cmd((dfstab_entry_t *)dfstab_ent, NULL, err);
        return (share_cmd);
} /* fs_get_Dfstab_share_cmd */

/*
 * edit_DFStab_ent - changes an entry in dfstab.
 */
fs_dfstab_entry_t
fs_edit_DFStab_ent(char *old_cmd, char *new_cmd, int *err)
{
        dfstab_entry_t *old_dfstabent, *new_dfstabent, *ret_val;

        if ((old_dfstabent =
            dfstab_line_to_dfstab_entry(old_cmd, err)) == NULL) {
                return (NULL);
        }
        if ((new_dfstabent =
            dfstab_line_to_dfstab_entry(new_cmd, err)) == NULL) {
                return (NULL);
        }
        if ((ret_val =
            change_dfstab_ent(old_dfstabent, new_dfstabent, err)) == NULL) {
                return (NULL);
        }
        free_dfstab_list(old_dfstabent);
        free_dfstab_list(new_dfstabent);
        return (ret_val);
}

/*
 * del_DFStab_ent - deletes an entry in dfstab.
 */
fs_dfstab_entry_t
fs_del_DFStab_ent(char *del_cmd, int *err)
{
        dfstab_entry_t *del_dfstabent, *ret_val;

        if ((del_dfstabent =
            dfstab_line_to_dfstab_entry(del_cmd, err)) == NULL) {
                return (NULL);
        }
        if ((ret_val =
            change_dfstab_ent(del_dfstabent, NULL, err)) == NULL) {
                return (NULL);
        }
        free_dfstab_list(del_dfstabent);
        return (ret_val);
}

/*
 * del_All_DFStab_ents_with_Path - deletes all duplicate entries with
 * the specified path.
 */
fs_dfstab_entry_t
fs_del_All_DFStab_ents_with_Path(char *path, int *err)
{
        dfstab_entry_t del_dfstabent, *ret_val;

        if (path != NULL) {
                if ((del_dfstabent.path = strdup(path)) != NULL) {
                        if ((ret_val = change_dfstab_ent(&del_dfstabent,
                            NULL, err)) == NULL) {
                                ret_val = NULL;
                        }
                        free(del_dfstabent.path);
                } else {
                        *err = ENOMEM;
                        ret_val = NULL;
                }
        } else {
                *err = EINVAL;
                ret_val = NULL;
        }
        return (ret_val);
}


int
fs_check_for_duplicate_DFStab_paths(char *path, int *err)
{
        dfstab_entry_t *dfstablist;
        int count = 0;

        *err = 0;
        if (path == NULL) {
                count = -1;
        }
        dfstablist = get_dfstab_ents(err);
        if (dfstablist != NULL) {
                while (dfstablist != NULL) {
                        if (strcmp(dfstablist->path, path) == 0) {
                                count++;
                        }
                        dfstablist = dfstablist->next;
                }

                free_dfstab_list(dfstablist);
        } else {
                if (err != 0)
                        count = *err;
                else
                        count = 0;
        }
        return (count);
}

void
fs_free_DFStab_ents(void *list)
{
        dfstab_entry_t *headp = (dfstab_entry_t *)list;
        free_dfstab_list(headp);
}

/*
 * used for debugging only
 */
void
fs_print_dfstab_entries(void *list)
{
        while (list != NULL) {

                if (fs_get_DFStab_ent_Fstype(list) != NULL)
                        printf("fstype: %s", fs_get_DFStab_ent_Fstype(list));
                if (fs_get_DFStab_ent_Desc(list) != NULL)
                        printf(" description: %s",
                            fs_get_DFStab_ent_Desc(list));
                if (fs_get_DFStab_ent_Options(list) != NULL)
                        printf(" options: %s",
                            fs_get_DFStab_ent_Options(list));
                if (fs_get_DFStab_ent_Path(list) != NULL)
                        printf(" shared path is: %s\n",
                            fs_get_DFStab_ent_Path(list));
                list = (void *)fs_get_DFStab_ent_Next(list);
        }

}