root/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.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 <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <limits.h>
#include <libnvpair.h>
#include <dlfcn.h>
#include <link.h>
#include <rp_plugin.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <sys/param.h>
#include <nfs/nfs4.h>
#include <rpcsvc/nfs4_prot.h>
#include "ref_subr.h"

extern int errno;

#define SERVICE_TYPE    "nfs-basic"

char *nfs_basic_service_type(void);
boolean_t nfs_basic_supports_svc(const char *);
int nfs_basic_deref(const char *, const char *, char *, size_t *);
int nfs_basic_form(const char *, const char *, char *, size_t *);

struct rp_plugin_ops rp_plugin_ops = {
        RP_PLUGIN_V1,
        NULL,                   /* rpo_init */
        NULL,                   /* rpo_fini */
        nfs_basic_service_type,
        nfs_basic_supports_svc,
        nfs_basic_form,
        nfs_basic_deref
};

/*
 * What service type does this module support?
 */
char *
nfs_basic_service_type()
{
        return (SERVICE_TYPE);
}

/*
 * Does this module support a particular service type?
 */
boolean_t
nfs_basic_supports_svc(const char *svc_type)
{
        if (!svc_type)
                return (0);
        return (!strncasecmp(svc_type, SERVICE_TYPE, strlen(SERVICE_TYPE)));
}

/*
 * Take a string with a set of locations like this:
 *   host1:/path1 host2:/path2 host3:/path3
 * and convert it to an fs_locations4 for the deref routine.
 */
static fs_locations4 *
get_fs_locations(char *buf)
{
        fs_locations4 *result = NULL;
        fs_location4 *fsl_array;
        int i, gothost;
        int fsl_count = 0, escape = 0, delimiter = 0;
        int len;
        char *p, *sp, *dp, buf2[SYMLINK_MAX];

        if (buf == NULL)
                return (NULL);
#ifdef DEBUG
        printf("get_fs_locations: input %s\n", buf);
#endif
        /*
         * Count fs_location entries by counting spaces.
         * Remember that escaped spaces ("\ ") may exist.
         * We mark the location boundaries with null bytes.
         * Variable use:
         *   escape -   set if we have found a backspace,
         *              part of either "\ " or "\\"
         *   delimiter - set if we have found a space and
         *               used to skip multiple spaces
         */
        for (sp = buf; sp && *sp; sp++) {
                if (*sp == '\\') {
                        escape = 1;
                        delimiter = 0;
                        continue;
                }
                if (*sp == ' ') {
                        if (delimiter == 1)
                                continue;
                        if (escape == 0) {
                                delimiter = 1;
                                fsl_count++;
                                *sp = '\0';
                        } else
                                escape = 0;
                } else
                        delimiter = 0;
        }
        len = sp - buf;
        sp--;
        if (escape == 0 && *sp != '\0')
                fsl_count++;
#ifdef DEBUG
        printf("get_fs_locations: fsl_count %d\n", fsl_count);
#endif
        if (fsl_count == 0)
                goto out;

        /* Alloc space for everything */
        result = calloc(1, sizeof (fs_locations4));
        if (result == NULL)
                goto out;
        fsl_array = calloc(fsl_count, sizeof (fs_location4));
        if (fsl_array == NULL) {
                free(result);
                result = NULL;
                goto out;
        }
        result->locations.locations_len = fsl_count;
        result->locations.locations_val = fsl_array;
        result->fs_root.pathname4_len = 0;
        result->fs_root.pathname4_val = NULL;

        /*
         * Copy input, removing escapes from host:/path/to/my\ files
         */
        sp = buf;
        dp = buf2;
        bzero(buf2, sizeof (buf2));

        i = gothost = 0;
        while ((sp && *sp && (sp - buf < len)) || gothost) {

                if (!gothost) {
                        /* Drop leading spaces */
                        if (*sp == ' ') {
                                sp++;
                                continue;
                        }

                        /* Look for the rightmost colon for host */
                        p = strrchr(sp, ':');
                        if (!p) {
#ifdef DEBUG
                                printf("get_fs_locations: skipping %s\n", sp);
#endif
                                fsl_count--;
                                sp += strlen(sp) + 1;
                        } else {
                                bcopy(sp, dp, p - sp);
                                sp = p + 1;
#ifdef DEBUG
                                printf("get_fs_locations: host %s\n", buf2);
#endif
                                fsl_array[i].server.server_len = 1;
                                fsl_array[i].server.server_val =
                                    malloc(sizeof (utf8string));
                                if (fsl_array[i].server.server_val == NULL) {
                                        int j;

                                        free(result);
                                        result = NULL;
                                        for (j = 0; j < i; j++)
                                                free(fsl_array[j].
                                                    server.server_val);
                                        free(fsl_array);
                                        goto out;
                                }
                                str_to_utf8(buf2,
                                    fsl_array[i].server.server_val);
                                gothost = 1;
                                dp = buf2;
                                bzero(buf2, sizeof (buf2));
                        }
                        continue;
                }

                /* End of string should mean a pathname */
                if (*sp == '\0' && gothost) {
#ifdef DEBUG
                        printf("get_fs_locations: path %s\n", buf2);
#endif
                        (void) make_pathname4(buf2, &fsl_array[i].rootpath);
                        i++;
                        gothost = 0;
                        dp = buf2;
                        bzero(buf2, sizeof (buf2));
                        if (sp - buf < len)
                                sp++;
                        continue;
                }

                /* Skip a single escape character */
                if (*sp == '\\')
                        sp++;

                /* Plain char, just copy it */
                *dp++ = *sp++;
        }

        /*
         * If we're still expecting a path name, we don't have a
         * server:/path pair and should discard the server and
         * note that we got fewer locations than expected.
         */
        if (gothost) {
                fsl_count--;
                free(fsl_array[i].server.server_val);
                fsl_array[i].server.server_val = NULL;
                fsl_array[i].server.server_len = 0;
        }

        /*
         * If we have zero entries, we never got a whole server:/path
         * pair, and so cannot have anything else allocated.
         */
        if (fsl_count <= 0) {
                free(result);
                free(fsl_array);
                return (NULL);
        }

        /*
         * Make sure we reflect the right number of locations.
         */
        if (fsl_count < result->locations.locations_len)
                result->locations.locations_len = fsl_count;

out:
        return (result);
}

/*
 * Deref function for nfs-basic service type returns an fs_locations4.
 */
int
nfs_basic_deref(const char *svc_type, const char *svc_data, char *buf,
    size_t *bufsz)
{
        int slen, err;
        fs_locations4 *fsl;
        XDR xdr;

        if ((!svc_type) || (!svc_data) || (!buf) || (!bufsz) || (*bufsz == 0))
                return (EINVAL);

        if (strcasecmp(svc_type, SERVICE_TYPE))
                return (ENOTSUP);

        fsl = get_fs_locations((char *)svc_data);
        if (fsl == NULL)
                return (ENOENT);
#ifdef DEBUG
        printf("nfs_basic_deref: past get_fs_locations()\n");
#endif
        slen = xdr_sizeof(xdr_fs_locations4, (void *)fsl);
        if (slen > *bufsz) {
                *bufsz = slen;
                xdr_free(xdr_fs_locations4, (char *)fsl);
                return (EOVERFLOW);
        }
#ifdef DEBUG
        printf("nfs_basic_deref: past buffer check\n");
        print_referral_summary(fsl);
#endif
        xdrmem_create(&xdr, buf, *bufsz, XDR_ENCODE);
        err = xdr_fs_locations4(&xdr, fsl);
        XDR_DESTROY(&xdr);
        xdr_free(xdr_fs_locations4, (char *)fsl);
        if (err != TRUE)
                return (EINVAL);
        *bufsz = slen;
#ifdef DEBUG
        printf("nfs_basic_deref: past xdr_fs_locations4() and done\n");
#endif
        return (0);
}

/*
 * Form function for nfs-basic service type.
 */
int
nfs_basic_form(const char *svc_type, const char *svc_data, char *buf,
    size_t *bufsz)
{
        int slen;

        if ((!svc_type) || (!svc_data) || (!buf) || (*bufsz == 0))
                return (EINVAL);

        if (strcmp(svc_type, SERVICE_TYPE))
                return (ENOTSUP);

        slen = strlen(svc_data) + 1;
        if (slen > *bufsz) {
                *bufsz = slen;
                return (EOVERFLOW);
        }
        *bufsz = slen;
        strncpy(buf, svc_data, slen);
        return (0);
}