root/usr/src/lib/smbsrv/libfksmbsrv/common/fake_xattr.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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 * Copyright 2024 RackTop Systems, Inc.
 */

#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/debug.h>

#include <attr.h>
#include <libnvpair.h>

#include "vncache.h"

static uint64_t zero_times[2];

static int
getxva_parse_nvl(xvattr_t *xvap,
    xoptattr_t *xoap, nvlist_t *nvl);

/*
 * See similar code to parse the nvlist in:
 * uts/common/fs/xattr.c : xattr_file_write()
 */
int
fop__getxvattr(vnode_t *vp, xvattr_t *xvap)
{
        nvlist_t *nvl = NULL;
        xoptattr_t *xoap = NULL;
        int fd = vncache_getfd(vp);
        int error;

        if ((xoap = xva_getxoptattr(xvap)) == NULL) {
                return (EINVAL);
        }

        error = fgetattr(fd, XATTR_VIEW_READWRITE, &nvl);
        if (error == 0) {
                error = getxva_parse_nvl(xvap, xoap, nvl);
                nvlist_free(nvl);
                nvl = NULL;
        }

        /*
         * Also get the readonly attrs, but don't fail.
         */
        if (fgetattr(fd, XATTR_VIEW_READONLY, &nvl) == 0) {
                (void) getxva_parse_nvl(xvap, xoap, nvl);
                nvlist_free(nvl);
        }

        return (error);
}

static int
getxva_parse_nvl(xvattr_t *xvap,
    xoptattr_t *xoap, nvlist_t *nvl)
{
        nvpair_t *pair = NULL;
        int error;

        while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
                data_type_t type;
                f_attr_t attr;
                boolean_t value = B_FALSE;
                uint64_t *times = zero_times;
                uint_t nelems = 2;

                /*
                 * Validate the name and type of each attribute.
                 * Log any unknown names and continue.  This will
                 * help if additional attributes are added later.
                 */
                type = nvpair_type(pair);
                attr = name_to_attr(nvpair_name(pair));
                if (attr == F_ATTR_INVAL)
                        continue;

                /*
                 * Verify nvlist type matches required type and view is OK
                 */

                if (type != attr_to_data_type(attr) ||
                    (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY))
                        continue;

                /*
                 * For OWNERSID/GROUPSID, just skip.
                 */
                if ((attr == F_OWNERSID || attr == F_GROUPSID))
                        continue;

                /*
                 * Retrieve data from nvpair
                 */
                switch (type) {
                case DATA_TYPE_BOOLEAN_VALUE:
                        if (nvpair_value_boolean_value(pair, &value)) {
                                error = EINVAL;
                                goto out;
                        }
                        break;

                case DATA_TYPE_UINT64_ARRAY:
                        if (nvpair_value_uint64_array(pair, &times, &nelems)) {
                                error = EINVAL;
                                goto out;
                        }
                        if (nelems < 2)
                                continue;
                        break;

                case DATA_TYPE_NVLIST:
                        continue;

                case DATA_TYPE_UINT8_ARRAY:
                        continue;

                default:
                        error = EINVAL;
                        goto out;
                }

                switch (attr) {
                /*
                 * If we have several similar optional attributes to
                 * process then we should do it all together here so that
                 * xoap and the requested bitmap can be set in one place.
                 */
                case F_READONLY:
                        XVA_SET_RTN(xvap, XAT_READONLY);
                        xoap->xoa_readonly = value;
                        break;

                case F_HIDDEN:
                        XVA_SET_RTN(xvap, XAT_HIDDEN);
                        xoap->xoa_hidden = value;
                        break;

                case F_SYSTEM:
                        XVA_SET_RTN(xvap, XAT_SYSTEM);
                        xoap->xoa_system = value;
                        break;

                case F_ARCHIVE:
                        XVA_SET_RTN(xvap, XAT_ARCHIVE);
                        xoap->xoa_archive = value;
                        break;

                case F_CRTIME:
                        XVA_SET_RTN(xvap, XAT_CREATETIME);
                        xoap->xoa_createtime.tv_sec = times[0];
                        xoap->xoa_createtime.tv_nsec = times[1];
                        break;

                case F_REPARSE:
                        XVA_SET_RTN(xvap, XAT_REPARSE);
                        xoap->xoa_reparse = value;
                        break;

                case F_OFFLINE:
                        XVA_SET_RTN(xvap, XAT_OFFLINE);
                        xoap->xoa_offline = value;
                        break;

                case F_SPARSE:
                        XVA_SET_RTN(xvap, XAT_SPARSE);
                        xoap->xoa_sparse = value;
                        break;

                default:
                        break;
                }
        }
        error = 0;

out:
        return (error);
}

/*
 * See similar code to build the nvlist in:
 * uts/common/fs/xattr.c : xattr_fill_nvlist()
 */
int
fop__setxvattr(vnode_t *vp, xvattr_t *xvap)
{
        uint64_t times[2];
        nvlist_t *nvl;
        xoptattr_t *xoap;       /* Pointer to optional attributes */
        int fd = vncache_getfd(vp);
        int error;

        if ((xoap = xva_getxoptattr(xvap)) == NULL)
                return (EINVAL);

        if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
                return (ENOMEM);

        if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_READONLY),
                    xoap->xoa_readonly) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_HIDDEN),
                    xoap->xoa_hidden) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_SYSTEM),
                    xoap->xoa_system) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_ARCHIVE),
                    xoap->xoa_archive) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
                times[0] = xoap->xoa_createtime.tv_sec;
                times[1] = xoap->xoa_createtime.tv_nsec;
                VERIFY(nvlist_add_uint64_array(nvl,
                    attr_to_name(F_CRTIME),
                    times, 2) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_REPARSE),
                    xoap->xoa_reparse) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_OFFLINE),
                    xoap->xoa_offline) == 0);
        }

        if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) {
                VERIFY(nvlist_add_boolean_value(nvl,
                    attr_to_name(F_SPARSE),
                    xoap->xoa_sparse) == 0);
        }

        error = fsetattr(fd, XATTR_VIEW_READWRITE, nvl);

        nvlist_free(nvl);

        return (error);
}