hackedteam/core-android-native

View on GitHub
selinux_native/jni/libsepol/src/write.c

Summary

Maintainability
Test Coverage

/* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */

/*
 * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
 *
 *    Support for enhanced MLS infrastructure.
 *
 * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
 *
 *     Added conditional policy language extensions
 * 
 * Updated: Joshua Brindle <jbrindle@tresys.com> and Jason Tang <jtang@tresys.org>
 *
 *    Module writing support
 *
 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
 * Copyright (C) 2003-2005 Tresys Technology, LLC
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include <assert.h>
#include <stdlib.h>

#include <sepol/policydb/ebitmap.h>
#include <sepol/policydb/avtab.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/expand.h>
#include <sepol/policydb/flask.h>

#include "debug.h"
#include "private.h"
#include "mls.h"
#include "log.h"

struct policy_data {
    struct policy_file *fp;
    struct policydb *p;
};

static int avrule_write_list(avrule_t * avrules, struct policy_file *fp);

static int ebitmap_write(ebitmap_t * e, struct policy_file *fp)
{
    ebitmap_node_t *n;
    uint32_t buf[32], bit, count;
    uint64_t map;
    size_t items;

    buf[0] = cpu_to_le32(MAPSIZE);
    buf[1] = cpu_to_le32(e->highbit);

    count = 0;
    for (n = e->node; n; n = n->next)
        count++;
    buf[2] = cpu_to_le32(count);

    items = put_entry(buf, sizeof(uint32_t), 3, fp);
    if (items != 3)
        return POLICYDB_ERROR;

    for (n = e->node; n; n = n->next) {
        bit = cpu_to_le32(n->startbit);
        items = put_entry(&bit, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        map = cpu_to_le64(n->map);
        items = put_entry(&map, sizeof(uint64_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;

    }

    return POLICYDB_SUCCESS;
}

/* Ordering of datums in the original avtab format in the policy file. */
static uint16_t spec_order[] = {
    AVTAB_ALLOWED,
    AVTAB_AUDITDENY,
    AVTAB_AUDITALLOW,
    AVTAB_TRANSITION,
    AVTAB_CHANGE,
    AVTAB_MEMBER
};

static int avtab_write_item(policydb_t * p,
                avtab_ptr_t cur, struct policy_file *fp,
                unsigned merge, unsigned commit, uint32_t * nel)
{
    avtab_ptr_t node;
    uint16_t buf16[4];
    uint32_t buf32[10], lookup, val;
    size_t items, items2;
    unsigned set;
    unsigned int oldvers = (p->policy_type == POLICY_KERN
                && p->policyvers < POLICYDB_VERSION_AVTAB);
    unsigned int i;

    if (oldvers) {
        /* Generate the old avtab format.
           Requires merging similar entries if uncond avtab. */
        if (merge) {
            if (cur->merged)
                return POLICYDB_SUCCESS;    /* already merged by prior merge */
        }

        items = 1;    /* item 0 is used for the item count */
        val = cur->key.source_type;
        buf32[items++] = cpu_to_le32(val);
        val = cur->key.target_type;
        buf32[items++] = cpu_to_le32(val);
        val = cur->key.target_class;
        buf32[items++] = cpu_to_le32(val);

        val = cur->key.specified & ~AVTAB_ENABLED;
        if (cur->key.specified & AVTAB_ENABLED)
            val |= AVTAB_ENABLED_OLD;
        set = 1;

        if (merge) {
            /* Merge specifier values for all similar (av or type)
               entries that have the same key. */
            if (val & AVTAB_AV)
                lookup = AVTAB_AV;
            else if (val & AVTAB_TYPE)
                lookup = AVTAB_TYPE;
            else
                return POLICYDB_ERROR;
            for (node = avtab_search_node_next(cur, lookup);
                 node;
                 node = avtab_search_node_next(node, lookup)) {
                val |= (node->key.specified & ~AVTAB_ENABLED);
                set++;
                if (node->key.specified & AVTAB_ENABLED)
                    val |= AVTAB_ENABLED_OLD;
            }
        }

        if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
            LOGD( "null entry");
            return POLICYDB_ERROR;
        }
        if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) {
            LOGD( "entry has both access "
                "vectors and types");
            return POLICYDB_ERROR;
        }

        buf32[items++] = cpu_to_le32(val);

        if (merge) {
            /* Include datums for all similar (av or type)
               entries that have the same key. */
            for (i = 0;
                 i < (sizeof(spec_order) / sizeof(spec_order[0]));
                 i++) {
                if (val & spec_order[i]) {
                    if (cur->key.specified & spec_order[i])
                        node = cur;
                    else {
                        node =
                            avtab_search_node_next(cur,
                                       spec_order
                                       [i]);
                        if (nel)
                            (*nel)--;    /* one less node */
                    }

                    if (!node) {
                        LOGD( "missing node");
                        return POLICYDB_ERROR;
                    }
                    buf32[items++] =
                        cpu_to_le32(node->datum.data);
                    set--;
                    node->merged = 1;
                }
            }
        } else {
            buf32[items++] = cpu_to_le32(cur->datum.data);
            cur->merged = 1;
            set--;
        }

        if (set) {
            LOGD( "data count wrong");
            return POLICYDB_ERROR;
        }

        buf32[0] = cpu_to_le32(items - 1);

        if (commit) {
            /* Commit this item to the policy file. */
            items2 = put_entry(buf32, sizeof(uint32_t), items, fp);
            if (items != items2)
                return POLICYDB_ERROR;
        }

        return POLICYDB_SUCCESS;
    }

    /* Generate the new avtab format. */
    buf16[0] = cpu_to_le16(cur->key.source_type);
    buf16[1] = cpu_to_le16(cur->key.target_type);
    buf16[2] = cpu_to_le16(cur->key.target_class);
    buf16[3] = cpu_to_le16(cur->key.specified);
    items = put_entry(buf16, sizeof(uint16_t), 4, fp);
    if (items != 4)
        return POLICYDB_ERROR;
    buf32[0] = cpu_to_le32(cur->datum.data);
    items = put_entry(buf32, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    return POLICYDB_SUCCESS;
}

static inline void avtab_reset_merged(avtab_t * a)
{
    unsigned int i;
    avtab_ptr_t cur;
    for (i = 0; i < a->nslot; i++) {
        for (cur = a->htable[i]; cur; cur = cur->next)
            cur->merged = 0;
    }
}

static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
{
    unsigned int i;
    int rc;
    avtab_t expa;
    avtab_ptr_t cur;
    uint32_t nel;
    size_t items;
    unsigned int oldvers = (p->policy_type == POLICY_KERN
                && p->policyvers < POLICYDB_VERSION_AVTAB);

    if (oldvers) {
        /* Old avtab format.
           First, we need to expand attributes.  Then, we need to
           merge similar entries, so we need to track merged nodes 
           and compute the final nel. */
        if (avtab_init(&expa))
            return POLICYDB_ERROR;
        if (expand_avtab(p, a, &expa)) {
            rc = -1;
            goto out;
        }
        a = &expa;
        avtab_reset_merged(a);
        nel = a->nel;
    } else {
        /* New avtab format.  nel is good to go. */
        nel = cpu_to_le32(a->nel);
        items = put_entry(&nel, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
    }

    for (i = 0; i < a->nslot; i++) {
        for (cur = a->htable[i]; cur; cur = cur->next) {
            /* If old format, compute final nel.
               If new format, write out the items. */
            if (avtab_write_item(p, cur, fp, 1, !oldvers, &nel)) {
                rc = -1;
                goto out;
            }
        }
    }

    if (oldvers) {
        /* Old avtab format.
           Write the computed nel value, then write the items. */
        nel = cpu_to_le32(nel);
        items = put_entry(&nel, sizeof(uint32_t), 1, fp);
        if (items != 1) {
            rc = -1;
            goto out;
        }
        avtab_reset_merged(a);
        for (i = 0; i < a->nslot; i++) {
            for (cur = a->htable[i]; cur; cur = cur->next) {
                if (avtab_write_item(p, cur, fp, 1, 1, NULL)) {
                    rc = -1;
                    goto out;
                }
            }
        }
    }

    rc = 0;
      out:
    if (oldvers)
        avtab_destroy(&expa);
    return rc;
}

/*
 * Write a semantic MLS level structure to a policydb binary 
 * representation file.
 */
static int mls_write_semantic_level_helper(mls_semantic_level_t * l,
                       struct policy_file *fp)
{
    uint32_t buf[2], ncat = 0;
    size_t items;
    mls_semantic_cat_t *cat;

    for (cat = l->cat; cat; cat = cat->next)
        ncat++;

    buf[0] = cpu_to_le32(l->sens);
    buf[1] = cpu_to_le32(ncat);
    items = put_entry(buf, sizeof(uint32_t), 2, fp);
    if (items != 2)
        return POLICYDB_ERROR;

    for (cat = l->cat; cat; cat = cat->next) {
        buf[0] = cpu_to_le32(cat->low);
        buf[1] = cpu_to_le32(cat->high);
        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

/*
 * Read a semantic MLS range structure to a policydb binary 
 * representation file.
 */
static int mls_write_semantic_range_helper(mls_semantic_range_t * r,
                       struct policy_file *fp)
{
    int rc;

    rc = mls_write_semantic_level_helper(&r->level[0], fp);
    if (rc)
        return rc;

    rc = mls_write_semantic_level_helper(&r->level[1], fp);

    return rc;
}

/*
 * Write a MLS level structure to a policydb binary 
 * representation file.
 */
static int mls_write_level(mls_level_t * l, struct policy_file *fp)
{
    uint32_t sens;
    size_t items;

    sens = cpu_to_le32(l->sens);
    items = put_entry(&sens, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    if (ebitmap_write(&l->cat, fp))
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

/*
 * Write a MLS range structure to a policydb binary 
 * representation file.
 */
static int mls_write_range_helper(mls_range_t * r, struct policy_file *fp)
{
    uint32_t buf[3];
    size_t items, items2;
    int eq;

    eq = mls_level_eq(&r->level[1], &r->level[0]);

    items = 1;        /* item 0 is used for the item count */
    buf[items++] = cpu_to_le32(r->level[0].sens);
    if (!eq)
        buf[items++] = cpu_to_le32(r->level[1].sens);
    buf[0] = cpu_to_le32(items - 1);

    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items2 != items)
        return POLICYDB_ERROR;

    if (ebitmap_write(&r->level[0].cat, fp))
        return POLICYDB_ERROR;
    if (!eq)
        if (ebitmap_write(&r->level[1].cat, fp))
            return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int sens_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    level_datum_t *levdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;

    levdatum = (level_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(levdatum->isalias);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (mls_write_level(levdatum->level, fp))
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int cat_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    cat_datum_t *catdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;

    catdatum = (cat_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(catdatum->s.value);
    buf[items++] = cpu_to_le32(catdatum->isalias);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int role_trans_write(policydb_t *p, struct policy_file *fp)
{
    role_trans_t *r = p->role_tr;
    role_trans_t *tr;
    uint32_t buf[3];
    size_t nel, items;
    int new_roletr = (p->policy_type == POLICY_KERN &&
              p->policyvers >= POLICYDB_VERSION_ROLETRANS);
    int warning_issued = 0;

    nel = 0;
    for (tr = r; tr; tr = tr->next)
        if(new_roletr || tr->tclass == SECCLASS_PROCESS)
            nel++;

    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (tr = r; tr; tr = tr->next) {
        if (!new_roletr && tr->tclass != SECCLASS_PROCESS) {
            if (!warning_issued)
              LOGD("Discarding role_transition "
                     "rules for security classes other than "
                     "\"process\"");
            warning_issued = 1;
            continue;
        }
        buf[0] = cpu_to_le32(tr->role);
        buf[1] = cpu_to_le32(tr->type);
        buf[2] = cpu_to_le32(tr->new_role);
        items = put_entry(buf, sizeof(uint32_t), 3, fp);
        if (items != 3)
            return POLICYDB_ERROR;
        if (new_roletr) {
            buf[0] = cpu_to_le32(tr->tclass);
            items = put_entry(buf, sizeof(uint32_t), 1, fp);
            if (items != 1)
                return POLICYDB_ERROR;
        }
    }

    return POLICYDB_SUCCESS;
}

static int role_allow_write(role_allow_t * r, struct policy_file *fp)
{
    role_allow_t *ra;
    uint32_t buf[2];
    size_t nel, items;

    nel = 0;
    for (ra = r; ra; ra = ra->next)
        nel++;
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (ra = r; ra; ra = ra->next) {
        buf[0] = cpu_to_le32(ra->role);
        buf[1] = cpu_to_le32(ra->new_role);
        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

static int filename_trans_write(filename_trans_t * r, struct policy_file *fp)
{
    filename_trans_t *ft;
    uint32_t buf[4];
    size_t nel, items, len;

    nel = 0;
    for (ft = r; ft; ft = ft->next)
        nel++;
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (ft = r; ft; ft = ft->next) {
        len = strlen(ft->name);
        buf[0] = cpu_to_le32(len);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;

        items = put_entry(ft->name, sizeof(char), len, fp);
        if (items != len)
            return POLICYDB_ERROR;

        buf[0] = cpu_to_le32(ft->stype);
        buf[1] = cpu_to_le32(ft->ttype);
        buf[2] = cpu_to_le32(ft->tclass);
        buf[3] = cpu_to_le32(ft->otype);
        items = put_entry(buf, sizeof(uint32_t), 4, fp);
        if (items != 4)
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

static int role_set_write(role_set_t * x, struct policy_file *fp)
{
    size_t items;
    uint32_t buf[1];

    if (ebitmap_write(&x->roles, fp))
        return POLICYDB_ERROR;

    buf[0] = cpu_to_le32(x->flags);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int type_set_write(type_set_t * x, struct policy_file *fp)
{
    size_t items;
    uint32_t buf[1];

    if (ebitmap_write(&x->types, fp))
        return POLICYDB_ERROR;
    if (ebitmap_write(&x->negset, fp))
        return POLICYDB_ERROR;

    buf[0] = cpu_to_le32(x->flags);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    cond_bool_datum_t *booldatum;
    uint32_t buf[3], len;
    unsigned int items, items2;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    struct policydb *p = pd->p;

    booldatum = (cond_bool_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(booldatum->s.value);
    buf[items++] = cpu_to_le32(booldatum->state);
    buf[items++] = cpu_to_le32(len);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;
    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (p->policy_type != POLICY_KERN &&
        p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
        buf[0] = cpu_to_le32(booldatum->flags);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

/*
 * cond_write_cond_av_list doesn't write out the av_list nodes.
 * Instead it writes out the key/value pairs from the avtab. This
 * is necessary because there is no way to uniquely identifying rules
 * in the avtab so it is not possible to associate individual rules
 * in the avtab with a conditional without saving them as part of
 * the conditional. This means that the avtab with the conditional
 * rules will not be saved but will be rebuilt on policy load.
 */
static int cond_write_av_list(policydb_t * p,
                  cond_av_list_t * list, struct policy_file *fp)
{
    uint32_t buf[4];
    cond_av_list_t *cur_list, *new_list = NULL;
    avtab_t expa;
    uint32_t len, items;
    unsigned int oldvers = (p->policy_type == POLICY_KERN
                && p->policyvers < POLICYDB_VERSION_AVTAB);
    int rc = -1;

    if (oldvers) {
        if (avtab_init(&expa))
            return POLICYDB_ERROR;
        if (expand_cond_av_list(p, list, &new_list, &expa))
            goto out;
        list = new_list;
    }

    len = 0;
    for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
        if (cur_list->node->parse_context)
            len++;
    }

    buf[0] = cpu_to_le32(len);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        goto out;

    if (len == 0) {
        rc = 0;
        goto out;
    }

    for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
        if (cur_list->node->parse_context)
            if (avtab_write_item(p, cur_list->node, fp, 0, 1, NULL))
                goto out;
    }

    rc = 0;
      out:
    if (oldvers) {
        cond_av_list_destroy(new_list);
        avtab_destroy(&expa);
    }

    return rc;
}

static int cond_write_node(policydb_t * p,
               cond_node_t * node, struct policy_file *fp)
{
    cond_expr_t *cur_expr;
    uint32_t buf[2];
    uint32_t items, items2, len;

    buf[0] = cpu_to_le32(node->cur_state);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    /* expr */
    len = 0;
    for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next)
        len++;

    buf[0] = cpu_to_le32(len);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) {
        items = 0;
        buf[items++] = cpu_to_le32(cur_expr->expr_type);
        buf[items++] = cpu_to_le32(cur_expr->bool);
        items2 = put_entry(buf, sizeof(uint32_t), items, fp);
        if (items2 != items)
            return POLICYDB_ERROR;
    }

    if (p->policy_type == POLICY_KERN) {
        if (cond_write_av_list(p, node->true_list, fp) != 0)
            return POLICYDB_ERROR;
        if (cond_write_av_list(p, node->false_list, fp) != 0)
            return POLICYDB_ERROR;
    } else {
        if (avrule_write_list(node->avtrue_list, fp))
            return POLICYDB_ERROR;
        if (avrule_write_list(node->avfalse_list, fp))
            return POLICYDB_ERROR;
    }

    if (p->policy_type != POLICY_KERN &&
        p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) {
        buf[0] = cpu_to_le32(node->flags);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

static int cond_write_list(policydb_t * p, cond_list_t * list,
               struct policy_file *fp)
{
    cond_node_t *cur;
    uint32_t len, items;
    uint32_t buf[1];

    len = 0;
    for (cur = list; cur != NULL; cur = cur->next)
        len++;
    buf[0] = cpu_to_le32(len);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    for (cur = list; cur != NULL; cur = cur->next) {
        if (cond_write_node(p, cur, fp) != 0)
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

/*
 * Write a security context structure
 * to a policydb binary representation file.
 */
static int context_write(struct policydb *p, context_struct_t * c,
             struct policy_file *fp)
{
    uint32_t buf[32];
    size_t items, items2;

    items = 0;
    buf[items++] = cpu_to_le32(c->user);
    buf[items++] = cpu_to_le32(c->role);
    buf[items++] = cpu_to_le32(c->type);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items2 != items)
        return POLICYDB_ERROR;
    if ((p->policyvers >= POLICYDB_VERSION_MLS
         && p->policy_type == POLICY_KERN)
        || (p->policyvers >= MOD_POLICYDB_VERSION_MLS
        && p->policy_type == POLICY_BASE))
        if (mls_write_range_helper(&c->range, fp))
            return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

/*
 * The following *_write functions are used to
 * write the symbol data to a policy database
 * binary representation file.
 */

static int perm_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    perm_datum_t *perdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;

    perdatum = (perm_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(perdatum->s.value);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int common_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    common_datum_t *comdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;

    comdatum = (common_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(comdatum->s.value);
    buf[items++] = cpu_to_le32(comdatum->permissions.nprim);
    buf[items++] = cpu_to_le32(comdatum->permissions.table->nel);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (hashtab_map(comdatum->permissions.table, perm_write, pd))
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int write_cons_helper(policydb_t * p,
                 constraint_node_t * node, int allowxtarget,
                 struct policy_file *fp)
{
    constraint_node_t *c;
    constraint_expr_t *e;
    uint32_t buf[3], nexpr;
    int items;

    for (c = node; c; c = c->next) {
        nexpr = 0;
        for (e = c->expr; e; e = e->next) {
            nexpr++;
        }
        buf[0] = cpu_to_le32(c->permissions);
        buf[1] = cpu_to_le32(nexpr);
        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
        for (e = c->expr; e; e = e->next) {
            items = 0;
            buf[0] = cpu_to_le32(e->expr_type);
            buf[1] = cpu_to_le32(e->attr);
            buf[2] = cpu_to_le32(e->op);
            items = put_entry(buf, sizeof(uint32_t), 3, fp);
            if (items != 3)
                return POLICYDB_ERROR;

            switch (e->expr_type) {
            case CEXPR_NAMES:
                if (!allowxtarget && (e->attr & CEXPR_XTARGET))
                    return POLICYDB_ERROR;
                if (ebitmap_write(&e->names, fp)) {
                    return POLICYDB_ERROR;
                }
                if ((p->policy_type != POLICY_KERN &&
                        type_set_write(e->type_names, fp)) ||
                        (p->policy_type == POLICY_KERN &&
                        (p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES) &&
                        type_set_write(e->type_names, fp))) {
                    return POLICYDB_ERROR;
                }
                break;
            default:
                break;
            }
        }
    }

    return POLICYDB_SUCCESS;
}

static int class_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    class_datum_t *cladatum;
    constraint_node_t *c;
    uint32_t buf[32], ncons;
    size_t items, items2, len, len2;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    struct policydb *p = pd->p;

    cladatum = (class_datum_t *) datum;

    len = strlen(key);
    if (cladatum->comkey)
        len2 = strlen(cladatum->comkey);
    else
        len2 = 0;

    ncons = 0;
    for (c = cladatum->constraints; c; c = c->next) {
        ncons++;
    }

    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(len2);
    buf[items++] = cpu_to_le32(cladatum->s.value);
    buf[items++] = cpu_to_le32(cladatum->permissions.nprim);
    if (cladatum->permissions.table)
        buf[items++] = cpu_to_le32(cladatum->permissions.table->nel);
    else
        buf[items++] = 0;
    buf[items++] = cpu_to_le32(ncons);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (cladatum->comkey) {
        items = put_entry(cladatum->comkey, 1, len2, fp);
        if (items != len2)
            return POLICYDB_ERROR;
    }
    if (hashtab_map(cladatum->permissions.table, perm_write, pd))
        return POLICYDB_ERROR;

    if (write_cons_helper(p, cladatum->constraints, 0, fp))
        return POLICYDB_ERROR;

    if ((p->policy_type == POLICY_KERN
         && p->policyvers >= POLICYDB_VERSION_VALIDATETRANS)
        || (p->policy_type == POLICY_BASE
        && p->policyvers >= MOD_POLICYDB_VERSION_VALIDATETRANS)) {
        /* write out the validatetrans rule */
        ncons = 0;
        for (c = cladatum->validatetrans; c; c = c->next) {
            ncons++;
        }
        buf[0] = cpu_to_le32(ncons);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        if (write_cons_helper(p, cladatum->validatetrans, 1, fp))
            return POLICYDB_ERROR;
    }

    if ((p->policy_type == POLICY_KERN &&
         p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) ||
        (p->policy_type == POLICY_BASE &&
         p->policyvers >= MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS)) {
        buf[0] = cpu_to_le32(cladatum->default_user);
        buf[1] = cpu_to_le32(cladatum->default_role);
        buf[2] = cpu_to_le32(cladatum->default_range);
        items = put_entry(buf, sizeof(uint32_t), 3, fp);
        if (items != 3)
            return POLICYDB_ERROR;
    }

    if ((p->policy_type == POLICY_KERN &&
         p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) ||
        (p->policy_type == POLICY_BASE &&
         p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) {
        buf[0] = cpu_to_le32(cladatum->default_type);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

static int role_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    role_datum_t *role;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    struct policydb *p = pd->p;

    role = (role_datum_t *) datum;

    /*
     * Role attributes are redundant for policy.X, skip them
     * when writing the roles symbol table. They are also skipped
     * when pp is downgraded.
     *
     * Their numbers would be deducted in policydb_write().
     */
    if ((role->flavor == ROLE_ATTRIB) &&
        ((p->policy_type == POLICY_KERN) ||
         (p->policy_type != POLICY_KERN &&
          p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB)))
        return POLICYDB_SUCCESS;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(role->s.value);
    if (policydb_has_boundary_feature(p))
        buf[items++] = cpu_to_le32(role->bounds);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (ebitmap_write(&role->dominates, fp))
        return POLICYDB_ERROR;
    if (p->policy_type == POLICY_KERN) {
        if (ebitmap_write(&role->types.types, fp))
            return POLICYDB_ERROR;
    } else {
        if (type_set_write(&role->types, fp))
            return POLICYDB_ERROR;
    }

    if (p->policy_type != POLICY_KERN &&
        p->policyvers >= MOD_POLICYDB_VERSION_ROLEATTRIB) {
        buf[0] = cpu_to_le32(role->flavor);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;

        if (ebitmap_write(&role->roles, fp))
            return POLICYDB_ERROR;
    }

    return POLICYDB_SUCCESS;
}

static int type_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    type_datum_t *typdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    struct policydb *p = pd->p;

    typdatum = (type_datum_t *) datum;

    /*
     * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY)
     * does not support to load entries of attribute, so we skip to write it.
     */
    if (p->policy_type == POLICY_KERN
        && p->policyvers < POLICYDB_VERSION_BOUNDARY
        && typdatum->flavor == TYPE_ATTRIB)
        return POLICYDB_SUCCESS;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(typdatum->s.value);
    if (policydb_has_boundary_feature(p)) {
        uint32_t properties = 0;

        if (p->policy_type != POLICY_KERN
            && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY_ALIAS) {
            buf[items++] = cpu_to_le32(typdatum->primary);
        }

        if (typdatum->primary)
            properties |= TYPEDATUM_PROPERTY_PRIMARY;

        if (typdatum->flavor == TYPE_ATTRIB) {
            properties |= TYPEDATUM_PROPERTY_ATTRIBUTE;
        } else if (typdatum->flavor == TYPE_ALIAS
               && p->policy_type != POLICY_KERN)
            properties |= TYPEDATUM_PROPERTY_ALIAS;

        if (typdatum->flags & TYPE_FLAGS_PERMISSIVE
            && p->policy_type != POLICY_KERN)
            properties |= TYPEDATUM_PROPERTY_PERMISSIVE;

        buf[items++] = cpu_to_le32(properties);
        buf[items++] = cpu_to_le32(typdatum->bounds);
    } else {
        buf[items++] = cpu_to_le32(typdatum->primary);

        if (p->policy_type != POLICY_KERN) {
            buf[items++] = cpu_to_le32(typdatum->flavor);

            if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE)
                buf[items++] = cpu_to_le32(typdatum->flags);
            else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE)
              LOGD("Warning! Module policy "
                     "version %d cannot support permissive "
                     "types, but one was defined",
                     p->policyvers);
        }
    }
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    if (p->policy_type != POLICY_KERN) {
        if (ebitmap_write(&typdatum->types, fp))
            return POLICYDB_ERROR;
    }

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    return POLICYDB_SUCCESS;
}

static int user_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    user_datum_t *usrdatum;
    uint32_t buf[32];
    size_t items, items2, len;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    struct policydb *p = pd->p;

    usrdatum = (user_datum_t *) datum;

    len = strlen(key);
    items = 0;
    buf[items++] = cpu_to_le32(len);
    buf[items++] = cpu_to_le32(usrdatum->s.value);
    if (policydb_has_boundary_feature(p))
        buf[items++] = cpu_to_le32(usrdatum->bounds);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    items = put_entry(key, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    if (p->policy_type == POLICY_KERN) {
        if (ebitmap_write(&usrdatum->roles.roles, fp))
            return POLICYDB_ERROR;
    } else {
        if (role_set_write(&usrdatum->roles, fp))
            return POLICYDB_ERROR;
    }

    if ((p->policyvers >= POLICYDB_VERSION_MLS
         && p->policy_type == POLICY_KERN)
        || (p->policyvers >= MOD_POLICYDB_VERSION_MLS
        && p->policyvers < MOD_POLICYDB_VERSION_MLS_USERS
        && p->policy_type == POLICY_MOD)
        || (p->policyvers >= MOD_POLICYDB_VERSION_MLS
        && p->policyvers < MOD_POLICYDB_VERSION_MLS_USERS
        && p->policy_type == POLICY_BASE)) {
        if (mls_write_range_helper(&usrdatum->exp_range, fp))
            return POLICYDB_ERROR;
        if (mls_write_level(&usrdatum->exp_dfltlevel, fp))
            return POLICYDB_ERROR;
    } else if ((p->policyvers >= MOD_POLICYDB_VERSION_MLS_USERS
            && p->policy_type == POLICY_MOD)
           || (p->policyvers >= MOD_POLICYDB_VERSION_MLS_USERS
               && p->policy_type == POLICY_BASE)) {
        if (mls_write_semantic_range_helper(&usrdatum->range, fp))
            return -1;
        if (mls_write_semantic_level_helper(&usrdatum->dfltlevel, fp))
            return -1;
    }

    return POLICYDB_SUCCESS;
}

static int (*write_f[SYM_NUM]) (hashtab_key_t key, hashtab_datum_t datum,
                void *datap) = {
common_write, class_write, role_write, type_write, user_write,
        cond_write_bool, sens_write, cat_write,};

static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p,
              struct policy_file *fp)
{
    unsigned int i, j;
    size_t nel, items;
    uint32_t buf[32];
    ocontext_t *c;
    for (i = 0; i < info->ocon_num; i++) {
        nel = 0;
        for (c = p->ocontexts[i]; c; c = c->next)
            nel++;
        buf[0] = cpu_to_le32(nel);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        for (c = p->ocontexts[i]; c; c = c->next) {
            switch (i) {
            case OCON_XEN_ISID:
                buf[0] = cpu_to_le32(c->sid[0]);
                items = put_entry(buf, sizeof(uint32_t), 1, fp);
                if (items != 1)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_XEN_PIRQ:
                buf[0] = cpu_to_le32(c->u.pirq);
                items = put_entry(buf, sizeof(uint32_t), 1, fp);
                if (items != 1)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_XEN_IOPORT:
                buf[0] = c->u.ioport.low_ioport;
                buf[1] = c->u.ioport.high_ioport;
                for (j = 0; j < 2; j++)
                    buf[j] = cpu_to_le32(buf[j]);
                items = put_entry(buf, sizeof(uint32_t), 2, fp);
                if (items != 2)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_XEN_IOMEM:
                buf[0] = c->u.iomem.low_iomem;
                buf[1] = c->u.iomem.high_iomem;
                for (j = 0; j < 2; j++)
                    buf[j] = cpu_to_le32(buf[j]);
                items = put_entry(buf, sizeof(uint32_t), 2, fp);
                if (items != 2)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_XEN_PCIDEVICE:
                buf[0] = cpu_to_le32(c->u.device);
                items = put_entry(buf, sizeof(uint32_t), 1, fp);
                if (items != 1)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            }
        }
    }
    return POLICYDB_SUCCESS;
}

static int ocontext_write_selinux(struct policydb_compat_info *info,
    policydb_t *p, struct policy_file *fp)
{
    unsigned int i, j;
    size_t nel, items, len;
    uint32_t buf[32];
    ocontext_t *c;
    for (i = 0; i < info->ocon_num; i++) {
        nel = 0;
        for (c = p->ocontexts[i]; c; c = c->next)
            nel++;
        buf[0] = cpu_to_le32(nel);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        for (c = p->ocontexts[i]; c; c = c->next) {
            switch (i) {
            case OCON_ISID:
                buf[0] = cpu_to_le32(c->sid[0]);
                items = put_entry(buf, sizeof(uint32_t), 1, fp);
                if (items != 1)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_FS:
            case OCON_NETIF:
                len = strlen(c->u.name);
                buf[0] = cpu_to_le32(len);
                items = put_entry(buf, sizeof(uint32_t), 1, fp);
                if (items != 1)
                    return POLICYDB_ERROR;
                items = put_entry(c->u.name, 1, len, fp);
                if (items != len)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[1], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_PORT:
                buf[0] = c->u.port.protocol;
                buf[1] = c->u.port.low_port;
                buf[2] = c->u.port.high_port;
                for (j = 0; j < 3; j++) {
                    buf[j] = cpu_to_le32(buf[j]);
                }
                items = put_entry(buf, sizeof(uint32_t), 3, fp);
                if (items != 3)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_NODE:
                buf[0] = c->u.node.addr; /* network order */
                buf[1] = c->u.node.mask; /* network order */
                items = put_entry(buf, sizeof(uint32_t), 2, fp);
                if (items != 2)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_FSUSE:
                buf[0] = cpu_to_le32(c->v.behavior);
                len = strlen(c->u.name);
                buf[1] = cpu_to_le32(len);
                items = put_entry(buf, sizeof(uint32_t), 2, fp);
                if (items != 2)
                    return POLICYDB_ERROR;
                items = put_entry(c->u.name, 1, len, fp);
                if (items != len)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            case OCON_NODE6:
                for (j = 0; j < 4; j++)
                    buf[j] = c->u.node6.addr[j]; /* network order */
                for (j = 0; j < 4; j++)
                    buf[j + 4] = c->u.node6.mask[j]; /* network order */
                items = put_entry(buf, sizeof(uint32_t), 8, fp);
                if (items != 8)
                    return POLICYDB_ERROR;
                if (context_write(p, &c->context[0], fp))
                    return POLICYDB_ERROR;
                break;
            }
        }
    }
    return POLICYDB_SUCCESS;
}

static int ocontext_write(struct policydb_compat_info *info, policydb_t * p,
    struct policy_file *fp)
{
    int rc = POLICYDB_ERROR;
    switch (p->target_platform) {
    case SEPOL_TARGET_SELINUX:
        rc = ocontext_write_selinux(info, p, fp);
        break;
    case SEPOL_TARGET_XEN:
        rc = ocontext_write_xen(info, p, fp);
        break;
    }
    return rc;
}

static int genfs_write(policydb_t * p, struct policy_file *fp)
{
    genfs_t *genfs;
    ocontext_t *c;
    size_t nel = 0, items, len;
    uint32_t buf[32];

    for (genfs = p->genfs; genfs; genfs = genfs->next)
        nel++;
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (genfs = p->genfs; genfs; genfs = genfs->next) {
        len = strlen(genfs->fstype);
        buf[0] = cpu_to_le32(len);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        items = put_entry(genfs->fstype, 1, len, fp);
        if (items != len)
            return POLICYDB_ERROR;
        nel = 0;
        for (c = genfs->head; c; c = c->next)
            nel++;
        buf[0] = cpu_to_le32(nel);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        for (c = genfs->head; c; c = c->next) {
            len = strlen(c->u.name);
            buf[0] = cpu_to_le32(len);
            items = put_entry(buf, sizeof(uint32_t), 1, fp);
            if (items != 1)
                return POLICYDB_ERROR;
            items = put_entry(c->u.name, 1, len, fp);
            if (items != len)
                return POLICYDB_ERROR;
            buf[0] = cpu_to_le32(c->v.sclass);
            items = put_entry(buf, sizeof(uint32_t), 1, fp);
            if (items != 1)
                return POLICYDB_ERROR;
            if (context_write(p, &c->context[0], fp))
                return POLICYDB_ERROR;
        }
    }
    return POLICYDB_SUCCESS;
}

static int range_write(policydb_t * p, struct policy_file *fp)
{
    size_t nel, items;
    struct range_trans *rt;
    uint32_t buf[2];
    int new_rangetr = (p->policy_type == POLICY_KERN &&
               p->policyvers >= POLICYDB_VERSION_RANGETRANS);
    int warning_issued = 0;

    nel = 0;
    for (rt = p->range_tr; rt; rt = rt->next) {
        /* all range_transitions are written for the new format, only
           process related range_transitions are written for the old
           format, so count accordingly */
        if (new_rangetr || rt->target_class == SECCLASS_PROCESS)
            nel++;
    }
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (rt = p->range_tr; rt; rt = rt->next) {
        if (!new_rangetr && rt->target_class != SECCLASS_PROCESS) {
            if (!warning_issued)
              LOGD("Discarding range_transition "
                     "rules for security classes other than "
                     "\"process\"");
            warning_issued = 1;
            continue;
        }
        buf[0] = cpu_to_le32(rt->source_type);
        buf[1] = cpu_to_le32(rt->target_type);
        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
        if (new_rangetr) {
            buf[0] = cpu_to_le32(rt->target_class);
            items = put_entry(buf, sizeof(uint32_t), 1, fp);
            if (items != 1)
                return POLICYDB_ERROR;
        }
        if (mls_write_range_helper(&rt->target_range, fp))
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

/************** module writing functions below **************/

static int avrule_write(avrule_t * avrule, struct policy_file *fp)
{
    size_t items, items2;
    uint32_t buf[32], len;
    class_perm_node_t *cur;

    items = 0;
    buf[items++] = cpu_to_le32(avrule->specified);
    buf[items++] = cpu_to_le32(avrule->flags);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items2 != items)
        return POLICYDB_ERROR;

    if (type_set_write(&avrule->stypes, fp))
        return POLICYDB_ERROR;

    if (type_set_write(&avrule->ttypes, fp))
        return POLICYDB_ERROR;

    cur = avrule->perms;
    len = 0;
    while (cur) {
        len++;
        cur = cur->next;
    }
    items = 0;
    buf[items++] = cpu_to_le32(len);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items2 != items)
        return POLICYDB_ERROR;
    cur = avrule->perms;
    while (cur) {
        items = 0;
        buf[items++] = cpu_to_le32(cur->class);
        buf[items++] = cpu_to_le32(cur->data);
        items2 = put_entry(buf, sizeof(uint32_t), items, fp);
        if (items2 != items)
            return POLICYDB_ERROR;

        cur = cur->next;
    }

    return POLICYDB_SUCCESS;
}

static int avrule_write_list(avrule_t * avrules, struct policy_file *fp)
{
    uint32_t buf[32], len;
    avrule_t *avrule;

    avrule = avrules;
    len = 0;
    while (avrule) {
        len++;
        avrule = avrule->next;
    }

    buf[0] = cpu_to_le32(len);
    if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1)
        return POLICYDB_ERROR;

    avrule = avrules;
    while (avrule) {
        avrule_write(avrule, fp);
        avrule = avrule->next;
    }

    return POLICYDB_SUCCESS;
}

static int only_process(ebitmap_t *in)
{
    unsigned int i;
    ebitmap_node_t *node;

    ebitmap_for_each_bit(in, node, i) {
        if (ebitmap_node_get_bit(node, i) &&
            i != SECCLASS_PROCESS - 1)
            return 0;
    }
    return 1;
}

static int role_trans_rule_write(policydb_t *p, role_trans_rule_t * t,
                 struct policy_file *fp)
{
    int nel = 0;
    size_t items;
    uint32_t buf[1];
    role_trans_rule_t *tr;
    int warned = 0;
    int new_role = p->policyvers >= MOD_POLICYDB_VERSION_ROLETRANS;

    for (tr = t; tr; tr = tr->next)
        if (new_role || only_process(&tr->classes))
            nel++;

    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (tr = t; tr; tr = tr->next) {
        if (!new_role && !only_process(&tr->classes)) {
            if (!warned)
              LOGD("Discarding role_transition "
                    "rules for security classes other than "
                    "\"process\"");
            warned = 1;
            continue;
        }
        if (role_set_write(&tr->roles, fp))
            return POLICYDB_ERROR;
        if (type_set_write(&tr->types, fp))
            return POLICYDB_ERROR;
        if (new_role)
            if (ebitmap_write(&tr->classes, fp))
                return POLICYDB_ERROR;
        buf[0] = cpu_to_le32(tr->new_role);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

static int role_allow_rule_write(role_allow_rule_t * r, struct policy_file *fp)
{
    int nel = 0;
    size_t items;
    uint32_t buf[1];
    role_allow_rule_t *ra;

    for (ra = r; ra; ra = ra->next)
        nel++;
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (ra = r; ra; ra = ra->next) {
        if (role_set_write(&ra->roles, fp))
            return POLICYDB_ERROR;
        if (role_set_write(&ra->new_roles, fp))
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

static int filename_trans_rule_write(filename_trans_rule_t * t, struct policy_file *fp)
{
    int nel = 0;
    size_t items;
    uint32_t buf[2], len;
    filename_trans_rule_t *ftr;

    for (ftr = t; ftr; ftr = ftr->next)
        nel++;

    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;

    for (ftr = t; ftr; ftr = ftr->next) {
        len = strlen(ftr->name);
        buf[0] = cpu_to_le32(len);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;

        items = put_entry(ftr->name, sizeof(char), len, fp);
        if (items != len)
            return POLICYDB_ERROR;

        if (type_set_write(&ftr->stypes, fp))
            return POLICYDB_ERROR;
        if (type_set_write(&ftr->ttypes, fp))
            return POLICYDB_ERROR;

        buf[0] = cpu_to_le32(ftr->tclass);
        buf[1] = cpu_to_le32(ftr->otype);

        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

static int range_trans_rule_write(range_trans_rule_t * t,
                  struct policy_file *fp)
{
    int nel = 0;
    size_t items;
    uint32_t buf[1];
    range_trans_rule_t *rt;

    for (rt = t; rt; rt = rt->next)
        nel++;
    buf[0] = cpu_to_le32(nel);
    items = put_entry(buf, sizeof(uint32_t), 1, fp);
    if (items != 1)
        return POLICYDB_ERROR;
    for (rt = t; rt; rt = rt->next) {
        if (type_set_write(&rt->stypes, fp))
            return POLICYDB_ERROR;
        if (type_set_write(&rt->ttypes, fp))
            return POLICYDB_ERROR;
        if (ebitmap_write(&rt->tclasses, fp))
            return POLICYDB_ERROR;
        if (mls_write_semantic_range_helper(&rt->trange, fp))
            return POLICYDB_ERROR;
    }
    return POLICYDB_SUCCESS;
}

static int scope_index_write(scope_index_t * scope_index,
                 unsigned int num_scope_syms,
                 struct policy_file *fp)
{
    unsigned int i;
    uint32_t buf[1];
    for (i = 0; i < num_scope_syms; i++) {
        if (ebitmap_write(scope_index->scope + i, fp) == -1) {
            return POLICYDB_ERROR;
        }
    }
    buf[0] = cpu_to_le32(scope_index->class_perms_len);
    if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) {
        return POLICYDB_ERROR;
    }
    for (i = 0; i < scope_index->class_perms_len; i++) {
        if (ebitmap_write(scope_index->class_perms_map + i, fp) == -1) {
            return POLICYDB_ERROR;
        }
    }
    return POLICYDB_SUCCESS;
}

static int avrule_decl_write(avrule_decl_t * decl, int num_scope_syms,
                 policydb_t * p, struct policy_file *fp)
{
    struct policy_data pd;
    uint32_t buf[2];
    int i;
    buf[0] = cpu_to_le32(decl->decl_id);
    buf[1] = cpu_to_le32(decl->enabled);
    if (put_entry(buf, sizeof(uint32_t), 2, fp) != 2) {
        return POLICYDB_ERROR;
    }
    if (cond_write_list(p, decl->cond_list, fp) == -1 ||
        avrule_write_list(decl->avrules, fp) == -1 ||
        role_trans_rule_write(p, decl->role_tr_rules, fp) == -1 ||
        role_allow_rule_write(decl->role_allow_rules, fp) == -1) {
        return POLICYDB_ERROR;
    }

    if (p->policyvers >= MOD_POLICYDB_VERSION_FILENAME_TRANS &&
        filename_trans_rule_write(decl->filename_trans_rules, fp))
        return POLICYDB_ERROR;

    if (p->policyvers >= MOD_POLICYDB_VERSION_RANGETRANS &&
        range_trans_rule_write(decl->range_tr_rules, fp) == -1) {
        return POLICYDB_ERROR;
    }
    if (scope_index_write(&decl->required, num_scope_syms, fp) == -1 ||
        scope_index_write(&decl->declared, num_scope_syms, fp) == -1) {
        return POLICYDB_ERROR;
    }
    pd.fp = fp;
    pd.p = p;
    for (i = 0; i < num_scope_syms; i++) {
        buf[0] = cpu_to_le32(decl->symtab[i].nprim);
        buf[1] = cpu_to_le32(decl->symtab[i].table->nel);
        if (put_entry(buf, sizeof(uint32_t), 2, fp) != 2) {
            return POLICYDB_ERROR;
        }
        if (hashtab_map(decl->symtab[i].table, write_f[i], &pd)) {
            return POLICYDB_ERROR;
        }
    }
    return POLICYDB_SUCCESS;
}

static int avrule_block_write(avrule_block_t * block, int num_scope_syms,
                  policydb_t * p, struct policy_file *fp)
{
    /* first write a count of the total number of blocks */
    uint32_t buf[1], num_blocks = 0;
    avrule_block_t *cur;
    for (cur = block; cur != NULL; cur = cur->next) {
        num_blocks++;
    }
    buf[0] = cpu_to_le32(num_blocks);
    if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) {
        return POLICYDB_ERROR;
    }

    /* now write each block */
    for (cur = block; cur != NULL; cur = cur->next) {
        uint32_t num_decls = 0;
        avrule_decl_t *decl;
        /* write a count of number of branches */
        for (decl = cur->branch_list; decl != NULL; decl = decl->next) {
            num_decls++;
        }
        buf[0] = cpu_to_le32(num_decls);
        if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) {
            return POLICYDB_ERROR;
        }
        for (decl = cur->branch_list; decl != NULL; decl = decl->next) {
            if (avrule_decl_write(decl, num_scope_syms, p, fp) ==
                -1) {
                return POLICYDB_ERROR;
            }
        }
    }
    return POLICYDB_SUCCESS;
}

static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
{
    scope_datum_t *scope = (scope_datum_t *) datum;
    struct policy_data *pd = ptr;
    struct policy_file *fp = pd->fp;
    uint32_t static_buf[32], *dyn_buf = NULL, *buf;
    size_t key_len = strlen(key);
    unsigned int items = 2 + scope->decl_ids_len, i;
    int rc;

    buf = static_buf;
    if (items >= (sizeof(static_buf) / 4)) {
        /* too many things required, so dynamically create a
         * buffer.  this would have been easier with C99's
         * dynamic arrays... */
        rc = POLICYDB_ERROR;
        dyn_buf = malloc(items * sizeof(*dyn_buf));
        if (!dyn_buf)
            goto err;
        buf = dyn_buf;
    }
    buf[0] = cpu_to_le32(key_len);

    rc = POLICYDB_ERROR;
    if (put_entry(buf, sizeof(*buf), 1, fp) != 1 ||
        put_entry(key, 1, key_len, fp) != key_len)
        goto err;
    buf[0] = cpu_to_le32(scope->scope);
    buf[1] = cpu_to_le32(scope->decl_ids_len);

    for (i = 0; i < scope->decl_ids_len; i++)
        buf[2 + i] = cpu_to_le32(scope->decl_ids[i]);

    rc = POLICYDB_ERROR;
    if (put_entry(buf, sizeof(*buf), items, fp) != items)
        goto err;
    rc = POLICYDB_SUCCESS;
err:
    free(dyn_buf);
    return rc;
}

static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)),
                 hashtab_datum_t datum, void *args)
{
    type_datum_t *typdatum = datum;
    uint32_t *p_nel = args;

    if (typdatum->flavor == TYPE_ATTRIB) {
        /* uncount attribute from total number of types */
        (*p_nel)--;
    }
    return 0;
}

static int role_attr_uncount(hashtab_key_t key __attribute__ ((unused)),
                 hashtab_datum_t datum, void *args)
{
    role_datum_t *role = datum;
    uint32_t *p_nel = args;

    if (role->flavor == ROLE_ATTRIB) {
        /* uncount attribute from total number of roles */
        (*p_nel)--;
    }
    return 0;
}

/*
 * Write the configuration data in a policy database
 * structure to a policy database binary representation
 * file.
 */
int policydb_write(policydb_t * p, struct policy_file *fp)
{
    unsigned int i, num_syms;
    uint32_t buf[32], config;
    size_t items, items2, len;
    struct policydb_compat_info *info;
    struct policy_data pd;
    char *policydb_str;

    if (p->unsupported_format)
        return POLICYDB_UNSUPPORTED;

    pd.fp = fp;
    pd.p = p;

    config = 0;
    if (p->mls) {
        if ((p->policyvers < POLICYDB_VERSION_MLS &&
            p->policy_type == POLICY_KERN) ||
            (p->policyvers < MOD_POLICYDB_VERSION_MLS &&
            p->policy_type == POLICY_BASE) ||
            (p->policyvers < MOD_POLICYDB_VERSION_MLS &&
            p->policy_type == POLICY_MOD)) {
            LOGD( "policy version %d cannot support MLS",
                p->policyvers);
            return POLICYDB_ERROR;
        }
        config |= POLICYDB_CONFIG_MLS;
    }

    config |= (POLICYDB_CONFIG_UNKNOWN_MASK & p->handle_unknown);

    /* Write the magic number and string identifiers. */
    items = 0;
    if (p->policy_type == POLICY_KERN) {
        buf[items++] = cpu_to_le32(POLICYDB_MAGIC);
        len = strlen(policydb_target_strings[p->target_platform]);
        policydb_str = policydb_target_strings[p->target_platform];
    } else {
        buf[items++] = cpu_to_le32(POLICYDB_MOD_MAGIC);
        len = strlen(POLICYDB_MOD_STRING);
        policydb_str = POLICYDB_MOD_STRING;
    }
    buf[items++] = cpu_to_le32(len);
    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;
    items = put_entry(policydb_str, 1, len, fp);
    if (items != len)
        return POLICYDB_ERROR;

    /* Write the version, config, and table sizes. */
    items = 0;
    info = policydb_lookup_compat(p->policyvers, p->policy_type,
                    p->target_platform);
    if (!info) {
        LOGD( "compatibility lookup failed for policy "
            "version %d", p->policyvers);
        return POLICYDB_ERROR;
    }

    if (p->policy_type != POLICY_KERN) {
        buf[items++] = cpu_to_le32(p->policy_type);
    }
    buf[items++] = cpu_to_le32(p->policyvers);
    buf[items++] = cpu_to_le32(config);
    buf[items++] = cpu_to_le32(info->sym_num);
    buf[items++] = cpu_to_le32(info->ocon_num);

    items2 = put_entry(buf, sizeof(uint32_t), items, fp);
    if (items != items2)
        return POLICYDB_ERROR;

    if (p->policy_type == POLICY_MOD) {
        /* Write module name and version */
        len = strlen(p->name);
        buf[0] = cpu_to_le32(len);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        items = put_entry(p->name, 1, len, fp);
        if (items != len)
            return POLICYDB_ERROR;
        len = strlen(p->version);
        buf[0] = cpu_to_le32(len);
        items = put_entry(buf, sizeof(uint32_t), 1, fp);
        if (items != 1)
            return POLICYDB_ERROR;
        items = put_entry(p->version, 1, len, fp);
        if (items != len)
            return POLICYDB_ERROR;
    }

    if ((p->policyvers >= POLICYDB_VERSION_POLCAP &&
         p->policy_type == POLICY_KERN) ||
        (p->policyvers >= MOD_POLICYDB_VERSION_POLCAP &&
         p->policy_type == POLICY_BASE) ||
        (p->policyvers >= MOD_POLICYDB_VERSION_POLCAP &&
         p->policy_type == POLICY_MOD)) {
        if (ebitmap_write(&p->policycaps, fp) == -1)
            return POLICYDB_ERROR;
    }

    if (p->policyvers < POLICYDB_VERSION_PERMISSIVE &&
        p->policy_type == POLICY_KERN) {
        ebitmap_node_t *tnode;

        ebitmap_for_each_bit(&p->permissive_map, tnode, i) {
            if (ebitmap_node_get_bit(tnode, i)) {
              LOGD("Warning! Policy version %d cannot "
                     "support permissive types, but some were defined",
                     p->policyvers);
                break;
            }
        }
    }

    if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE &&
        p->policy_type == POLICY_KERN) {
        if (ebitmap_write(&p->permissive_map, fp) == -1)
            return POLICYDB_ERROR;
    }

    num_syms = info->sym_num;
    for (i = 0; i < num_syms; i++) {
        buf[0] = cpu_to_le32(p->symtab[i].nprim);
        buf[1] = p->symtab[i].table->nel;

        /*
         * A special case when writing type/attribute symbol table.
         * The kernel policy version less than 24 does not support
         * to load entries of attribute, so we have to re-calculate
         * the actual number of types except for attributes.
         */
        if (i == SYM_TYPES &&
            p->policyvers < POLICYDB_VERSION_BOUNDARY &&
            p->policy_type == POLICY_KERN) {
            hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]);
        }

        /*
         * Another special case when writing role/attribute symbol
         * table, role attributes are redundant for policy.X, or
         * when the pp's version is not big enough. So deduct
         * their numbers from p_roles.table->nel.
         */
        if ((i == SYM_ROLES) &&
            ((p->policy_type == POLICY_KERN) ||
             (p->policy_type != POLICY_KERN &&
              p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB)))
            (void)hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]);

        buf[1] = cpu_to_le32(buf[1]);
        items = put_entry(buf, sizeof(uint32_t), 2, fp);
        if (items != 2)
            return POLICYDB_ERROR;
        if (hashtab_map(p->symtab[i].table, write_f[i], &pd))
            return POLICYDB_ERROR;
    }

    if (p->policy_type == POLICY_KERN) {
        if (avtab_write(p, &p->te_avtab, fp))
            return POLICYDB_ERROR;
        if (p->policyvers < POLICYDB_VERSION_BOOL) {
            if (p->p_bools.nprim)
              LOGD("Discarding booleans and conditional rules");
        } else {
            if (cond_write_list(p, p->cond_list, fp))
                return POLICYDB_ERROR;
        }
        if (role_trans_write(p, fp))
            return POLICYDB_ERROR;
        if (role_allow_write(p->role_allow, fp))
            return POLICYDB_ERROR;
        if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS) {
            if (filename_trans_write(p->filename_trans, fp))
                return POLICYDB_ERROR;
        } else {
            if (p->filename_trans)
                LOGD( "Discarding filename type transition rules");
        }
    } else {
        if (avrule_block_write(p->global, num_syms, p, fp) == -1) {
            return POLICYDB_ERROR;
        }

        for (i = 0; i < num_syms; i++) {
            buf[0] = cpu_to_le32(p->scope[i].table->nel);
            if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) {
                return POLICYDB_ERROR;
            }
            if (hashtab_map(p->scope[i].table, scope_write, &pd))
                return POLICYDB_ERROR;
        }
    }

    if (ocontext_write(info, p, fp) == -1 || genfs_write(p, fp) == -1) {
        return POLICYDB_ERROR;
    }

    if ((p->policyvers >= POLICYDB_VERSION_MLS
         && p->policy_type == POLICY_KERN)
        || (p->policyvers >= MOD_POLICYDB_VERSION_MLS
        && p->policyvers < MOD_POLICYDB_VERSION_RANGETRANS
        && p->policy_type == POLICY_BASE)) {
        if (range_write(p, fp)) {
            return POLICYDB_ERROR;
        }
    }

    if (p->policy_type == POLICY_KERN
        && p->policyvers >= POLICYDB_VERSION_AVTAB) {
        for (i = 0; i < p->p_types.nprim; i++) {
            if (ebitmap_write(&p->type_attr_map[i], fp) == -1)
                return POLICYDB_ERROR;
        }
    }

    return POLICYDB_SUCCESS;
}