root/usr/src/lib/libc/port/gen/attrat.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "lint.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <mtlib.h>
#include <attr.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
#include <atomic.h>

static int (*nvpacker)(nvlist_t *, char **, size_t *, int, int);
static int (*nvsize)(nvlist_t *, size_t *, int);
static int (*nvunpacker)(char *, size_t, nvlist_t **);
static int (*nvfree)(nvlist_t *);
static int (*nvlookupint64)(nvlist_t *, const char *, uint64_t *);

static mutex_t attrlock = DEFAULTMUTEX;
static int initialized;

static char *xattr_view_name[XATTR_VIEW_LAST] = {
        VIEW_READONLY,
        VIEW_READWRITE
};

int
__openattrdirat(int fd, const char *name)
{
        return (syscall(SYS_openat, fd, name, FXATTRDIROPEN, 0));
}

static int
attrat_init()
{
        void *packer;
        void *sizer;
        void *unpacker;
        void *freer;
        void *looker;

        if (initialized == 0) {
                void *handle = dlopen("libnvpair.so.1", RTLD_LAZY);

                if (handle == NULL ||
                    (packer = dlsym(handle, "nvlist_pack")) == NULL ||
                    (sizer = dlsym(handle, "nvlist_size")) == NULL ||
                    (unpacker = dlsym(handle, "nvlist_unpack")) == NULL ||
                    (freer = dlsym(handle, "nvlist_free")) == NULL ||
                    (looker = dlsym(handle, "nvlist_lookup_uint64")) == NULL) {
                        if (handle)
                                (void) dlclose(handle);
                        return (-1);
                }

                lmutex_lock(&attrlock);

                if (initialized != 0) {
                        lmutex_unlock(&attrlock);
                        (void) dlclose(handle);
                        return (0);
                }

                nvpacker = (int (*)(nvlist_t *, char **, size_t *, int, int))
                    packer;
                nvsize = (int (*)(nvlist_t *, size_t *, int))
                    sizer;
                nvunpacker = (int (*)(char *, size_t, nvlist_t **))
                    unpacker;
                nvfree = (int (*)(nvlist_t *))
                    freer;
                nvlookupint64 = (int (*)(nvlist_t *, const char *, uint64_t *))
                    looker;

                membar_producer();
                initialized = 1;
                lmutex_unlock(&attrlock);
        }
        return (0);
}

static int
attr_nv_pack(nvlist_t *request, void **nv_request, size_t *nv_requestlen)
{
        size_t bufsize;
        char *packbuf = NULL;

        if (nvsize(request, &bufsize, NV_ENCODE_XDR) != 0) {
                errno = EINVAL;
                return (-1);
        }

        packbuf = malloc(bufsize);
        if (packbuf == NULL)
                return (-1);
        if (nvpacker(request, &packbuf, &bufsize, NV_ENCODE_XDR, 0) != 0) {
                free(packbuf);
                errno = EINVAL;
                return (-1);
        } else {
                *nv_request = (void *)packbuf;
                *nv_requestlen = bufsize;
        }
        return (0);
}

static const char *
view_to_name(xattr_view_t view)
{
        if (view >= XATTR_VIEW_LAST || view < 0)
                return (NULL);
        return (xattr_view_name[view]);
}

static int
xattr_openat(int basefd, xattr_view_t view, int mode)
{
        const char *xattrname;
        int xattrfd;
        int oflag;

        switch (view) {
        case XATTR_VIEW_READONLY:
                oflag = O_RDONLY;
                break;
        case XATTR_VIEW_READWRITE:
                oflag = mode & O_RDWR;
                break;
        default:
                errno = EINVAL;
                return (-1);
        }
        if (mode & O_XATTR)
                oflag |= O_XATTR;

        xattrname = view_to_name(view);
        xattrfd = openat(basefd, xattrname, oflag);
        if (xattrfd < 0)
                return (xattrfd);
        /* Don't cache sysattr info (advisory) */
        (void) directio(xattrfd, DIRECTIO_ON);
        return (xattrfd);
}

static int
cgetattr(int fd, nvlist_t **response)
{
        int error;
        int bytesread;
        void *nv_response;
        size_t nv_responselen;
        struct stat buf;

        if ((error = attrat_init()) != 0)
                return (error);
        if ((error = fstat(fd, &buf)) != 0)
                return (error);
        nv_responselen = buf.st_size;

        if ((nv_response = malloc(nv_responselen)) == NULL)
                return (-1);
        bytesread = read(fd, nv_response, nv_responselen);
        if (bytesread != nv_responselen) {
                free(nv_response);
                errno = EFAULT;
                return (-1);
        }

        if (nvunpacker(nv_response, nv_responselen, response)) {
                free(nv_response);
                errno = ENOMEM;
                return (-1);
        }

        free(nv_response);
        return (0);
}

static int
csetattr(int fd, nvlist_t *request)
{
        int error, saveerrno;
        int byteswritten;
        void *nv_request;
        size_t nv_requestlen;

        if ((error = attrat_init()) != 0)
                return (error);

        if ((error = attr_nv_pack(request, &nv_request, &nv_requestlen)) != 0)
                return (error);

        byteswritten = write(fd, nv_request, nv_requestlen);
        if (byteswritten != nv_requestlen) {
                saveerrno = errno;
                free(nv_request);
                errno = saveerrno;
                return (-1);
        }

        free(nv_request);
        return (0);
}

int
fgetattr(int basefd, xattr_view_t view, nvlist_t **response)
{
        int error, saveerrno, xattrfd;

        if ((xattrfd = xattr_openat(basefd, view, O_XATTR)) < 0)
                return (xattrfd);

        error = cgetattr(xattrfd, response);
        saveerrno = errno;
        (void) close(xattrfd);
        errno = saveerrno;
        return (error);
}

int
fsetattr(int basefd, xattr_view_t view, nvlist_t *request)
{
        int error, saveerrno, xattrfd;

        if ((xattrfd = xattr_openat(basefd, view, O_RDWR | O_XATTR)) < 0)
                return (xattrfd);
        error = csetattr(xattrfd, request);
        saveerrno = errno;
        (void) close(xattrfd);
        errno = saveerrno;
        return (error);
}

int
getattrat(int basefd, xattr_view_t view, const char *name, nvlist_t **response)
{
        int error, saveerrno, namefd, xattrfd;

        if ((namefd = __openattrdirat(basefd, name)) < 0)
                return (namefd);

        if ((xattrfd = xattr_openat(namefd, view, 0)) < 0) {
                saveerrno = errno;
                (void) close(namefd);
                errno = saveerrno;
                return (xattrfd);
        }

        error = cgetattr(xattrfd, response);
        saveerrno = errno;
        (void) close(namefd);
        (void) close(xattrfd);
        errno = saveerrno;
        return (error);
}

int
setattrat(int basefd, xattr_view_t view, const char *name, nvlist_t *request)
{
        int error, saveerrno, namefd, xattrfd;

        if ((namefd = __openattrdirat(basefd, name)) < 0)
                return (namefd);

        if ((xattrfd = xattr_openat(namefd, view, O_RDWR)) < 0) {
                saveerrno = errno;
                (void) close(namefd);
                errno = saveerrno;
                return (xattrfd);
        }

        error = csetattr(xattrfd, request);
        saveerrno = errno;
        (void) close(namefd);
        (void) close(xattrfd);
        errno = saveerrno;
        return (error);
}

void
libc_nvlist_free(nvlist_t *nvp)
{
        (void) nvfree(nvp);
}

int
libc_nvlist_lookup_uint64(nvlist_t *nvp, const char *name, uint64_t *value)
{
        return (nvlookupint64(nvp, name, value));
}