root/usr/src/lib/libsec/common/aclsort.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 (c) 1993-1997 by Sun Microsystems, Inc.
 * All rights reserved
 */

/*LINTLIBRARY*/

/*
 * aclsort():
 *      Sort an ACL by entry type according to the following order:
 *      USER_OBJ, USER, GROUP_OBJ, GROUP, CLASS_OBJ, OTHER_OBJ
 *      DEF_USER_OBJ, DEF_USER, DEF_GROUP_OBJ, DEF_GROUP, DEF_CLASS_OBJ,
 *      DEF_OTHER_OBJ.
 *      For USER, GROUP, DEF_USER, and DEF_GROUP entries, the entries
 *      are further sorted by ids.
 */

#include <stdlib.h>
#include <sys/acl.h>

#define TOTAL_ENTRY_TYPES       12

/*
 * This maps the entry defined value to a value for sorting.
 * These values may not be the same. It is easier to add an
 * entry type with this map.
 *
 * Because the defines and sorting order are not the same,
 * the following map_to_sort table is needed.
 */
struct map {
        int     sort_order;
        int     entry_type;
};

static struct map map_to_sort[] = {
                {0, 0}, /* UNUSED */
                {1,     USER_OBJ},
                {2,     USER},
                {3,     GROUP_OBJ},
                {4,     GROUP},
                {5,     CLASS_OBJ},
                {6,     OTHER_OBJ},
                {7,     DEF_USER_OBJ},
                {8,     DEF_USER},
                {9,     DEF_GROUP_OBJ},
                {10,    DEF_GROUP},
                {11,    DEF_CLASS_OBJ},
                {12,    DEF_OTHER_OBJ},
};

static int entrycmp(const aclent_t *, const aclent_t *);
static int idcmp(const aclent_t *, const aclent_t *);
static void sortid(aclent_t *, int, int);

int
aclsort(int nentries, int calcmask, aclent_t *aclbufp)
{
        aclent_t                *tp;
        unsigned int            newmask = 0;
        int                     which;
        int                     i;
        int                     k;

        /* check validity first before sorting */
        if (aclcheck(aclbufp, nentries, &which) != 0)
                return (-1);

        /*
         * Performance enhancement:
         * We change entry type to sort order in the ACL, do the sorting.
         * We then change sort order back to entry type.
         * This makes entrycmp() very "light" and thus improves performance.
         * Contrast to original implementation that had to find out
         * the sorting order each time it is called.
         */
        for (tp = aclbufp, i = 0; i < nentries; tp++, i++) {
                for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) {
                        if (tp->a_type == map_to_sort[k].entry_type) {
                                tp->a_type = map_to_sort[k].sort_order;
                                break;
                        }
                }
        }

        /* typecast to remove incompatible type warning */
        qsort(aclbufp, nentries, sizeof (aclent_t),
            (int (*)(const void *, const void *))entrycmp);

        for (tp = aclbufp, i = 0; i < nentries; tp++, i++) {
                for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) {
                        if (tp->a_type == map_to_sort[k].sort_order) {
                                tp->a_type = map_to_sort[k].entry_type;
                                break;
                        }
                }
        }

        /*
         * Start sorting id within USER and GROUP
         * sortid() could return a pointer and entries left
         * so that we dont have to search from the beginning
         *  every time it calls
         */
        sortid(aclbufp, nentries, USER);
        sortid(aclbufp, nentries, GROUP);
        sortid(aclbufp, nentries, DEF_USER);
        sortid(aclbufp, nentries, DEF_GROUP);

        /*
         * Recalculate mask entry
         */
        if (calcmask != 0) {
                /*
                 * At this point, ACL is valid and sorted. We may find a
                 * CLASS_OBJ entry and stop. Because of the case of minimum ACL,
                 * we still have to check till OTHER_OBJ entry is shown.
                 */
                for (tp = aclbufp; tp->a_type != OTHER_OBJ; tp++) {
                        if (tp->a_type == USER || tp->a_type == GROUP ||
                            tp->a_type == GROUP_OBJ)
                                newmask |= tp->a_perm;
                        if (tp->a_type == CLASS_OBJ)
                                break;
                }
                if (tp->a_type == CLASS_OBJ)
                        tp->a_perm = (unsigned char)newmask;
        }
        return (0);
}

/*
 * sortid() sorts the ids with the same entry type in increasing order
 */
static void
sortid(aclent_t *ap, int cnt, int type)
{
        aclent_t        *tp;
        aclent_t        *startp; /* start of the desired entry type */
        int             howmany;

        for (tp = ap; cnt-- > 0; tp++) {
                if (tp->a_type != type)
                        continue;
                startp = tp;
                howmany = 1;
                for (tp++, cnt--; cnt > 0 && tp->a_type == type; tp++, cnt--)
                        howmany++;
                /* typecast to remove incompatible type warning */
                qsort(startp, howmany, sizeof (aclent_t),
                    (int (*)(const void*, const void*))idcmp);
        }
}

/*
 * compare the field a_type
 */
static int
entrycmp(const aclent_t *i, const aclent_t *j)
{
        return ((int)(i->a_type) - (int)(j->a_type));
}

/*
 * compare the field a_id
 */
static int
idcmp(const aclent_t *i, const aclent_t *j)
{
        return ((int)(i->a_id) - (int)(j->a_id));
}