
/* Author : Stephen Smalley, <sds@tycho.nsa.gov> */

/*
 * 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
 * Copyright (C) 2017 Mellanox Technologies Inc.
 *
 *  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"

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

static int avrule_write_list(policydb_t *p,
			     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;
	uint8_t buf8;
	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))) {
			ERR(fp->handle, "null entry");
			return POLICYDB_ERROR;
		}
		if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) {
			ERR(fp->handle, "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) {
						ERR(fp->handle, "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) {
			ERR(fp->handle, "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;
	if ((p->policyvers < POLICYDB_VERSION_XPERMS_IOCTL) &&
			(cur->key.specified & AVTAB_XPERMS)) {
		ERR(fp->handle, "policy version %u does not support ioctl extended"
				"permissions rules and one was specified", p->policyvers);
		return POLICYDB_ERROR;
	}

	if (p->target_platform != SEPOL_TARGET_SELINUX &&
			(cur->key.specified & AVTAB_XPERMS)) {
		ERR(fp->handle, "Target platform %s does not support ioctl "
				"extended permissions rules and one was specified",
				policydb_target_strings[p->target_platform]);
		return POLICYDB_ERROR;
	}

	if (cur->key.specified & AVTAB_XPERMS) {
		buf8 = cur->datum.xperms->specified;
		items = put_entry(&buf8, sizeof(uint8_t),1,fp);
		if (items != 1)
			return POLICYDB_ERROR;
		buf8 = cur->datum.xperms->driver;
		items = put_entry(&buf8, sizeof(uint8_t),1,fp);
		if (items != 1)
			return POLICYDB_ERROR;
		for (i = 0; i < ARRAY_SIZE(cur->datum.xperms->perms); i++)
			buf32[i] = cpu_to_le32(cur->datum.xperms->perms[i]);
		items = put_entry(buf32, sizeof(uint32_t),8,fp);
		if (items != 8)
			return POLICYDB_ERROR;
	} else {
		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)
				WARN(fp->handle, "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_write_helper(hashtab_key_t key, void *data, void *ptr)
{
	uint32_t buf[4];
	size_t items, len;
	struct filename_trans *ft = (struct filename_trans *)key;
	struct filename_trans_datum *otype = data;
	void *fp = ptr;

	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(otype->otype);
	items = put_entry(buf, sizeof(uint32_t), 4, fp);
	if (items != 4)
		return POLICYDB_ERROR;

	return 0;
}

static int filename_trans_write(struct policydb *p, void *fp)
{
	size_t nel, items;
	uint32_t buf[1];
	int rc;

	if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
		return 0;

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

	rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
	if (rc)
		return rc;

	return 0;
}

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(p, node->avtrue_list, fp))
			return POLICYDB_ERROR;
		if (avrule_write_list(p, 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) {
			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 (role->s.value == OBJECT_R_VAL) {
			/*
			 * CIL populates object_r's types map
			 * rather than handling it as a special case.
			 * However, this creates an inconsistency with
			 * the kernel policy read from /sys/fs/selinux/policy
			 * because the kernel ignores everything except for
			 * object_r's value from the policy file.
			 * Make them consistent by writing an empty
			 * ebitmap instead.
			 */
			ebitmap_t empty;
			ebitmap_init(&empty);
			if (ebitmap_write(&empty, fp))
				return POLICYDB_ERROR;
		} else {
			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)
				WARN(fp->handle, "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, 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_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:
				if (p->policyvers >= POLICYDB_VERSION_XEN_DEVICETREE) {
					uint64_t b64[2];
					b64[0] = c->u.iomem.low_iomem;
					b64[1] = c->u.iomem.high_iomem;
					for (j = 0; j < 2; j++)
						b64[j] = cpu_to_le64(b64[j]);
					items = put_entry(b64, sizeof(uint64_t), 2, fp);
					if (items != 2)
						return POLICYDB_ERROR;
				} else {
					if (c->u.iomem.high_iomem > 0xFFFFFFFFULL) {
						ERR(fp->handle, "policy version %d"
							" cannot represent IOMEM addresses over 16TB",
							p->policyvers);
						return POLICYDB_ERROR;
					}

					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;
			case OCON_XEN_DEVICETREE:
				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;
				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_IBPKEY:
				 /* The subnet prefix is in network order */
				memcpy(buf, &c->u.ibpkey.subnet_prefix,
				       sizeof(c->u.ibpkey.subnet_prefix));

				buf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
				buf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);

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

				if (context_write(p, &c->context[0], fp))
					return POLICYDB_ERROR;
				break;
			case OCON_IBENDPORT:
				len = strlen(c->u.ibendport.dev_name);
				buf[0] = cpu_to_le32(len);
				buf[1] = cpu_to_le32(c->u.ibendport.port);
				items = put_entry(buf, sizeof(uint32_t), 2, fp);
				if (items != 2)
					return POLICYDB_ERROR;
				items = put_entry(c->u.ibendport.dev_name, 1, len, fp);
				if (items != len)
					return POLICYDB_ERROR;

				if (context_write(p, &c->context[0], 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;
}


struct rangetrans_write_args {
	size_t nel;
	int new_rangetr;
	struct policy_file *fp;
};

static int rangetrans_count(hashtab_key_t key,
			    void *data __attribute__ ((unused)),
			    void *ptr)
{
	struct range_trans *rt = (struct range_trans *)key;
	struct rangetrans_write_args *args = ptr;

	/* all range_transitions are written for the new format, only
	   process related range_transitions are written for the old
	   format, so count accordingly */
	if (args->new_rangetr || rt->target_class == SECCLASS_PROCESS)
		args->nel++;
	return 0;
}

static int range_write_helper(hashtab_key_t key, void *data, void *ptr)
{
	uint32_t buf[2];
	struct range_trans *rt = (struct range_trans *)key;
	struct mls_range *r = data;
	struct rangetrans_write_args *args = ptr;
	struct policy_file *fp = args->fp;
	int new_rangetr = args->new_rangetr;
	size_t items;
	static int warning_issued = 0;
	int rc;

	if (!new_rangetr && rt->target_class != SECCLASS_PROCESS) {
		if (!warning_issued)
			WARN(fp->handle, "Discarding range_transition "
			     "rules for security classes other than "
			     "\"process\"");
		warning_issued = 1;
		return 0;
	}

	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;
	}
	rc = mls_write_range_helper(r, fp);
	if (rc)
		return rc;

	return 0;
}

static int range_write(policydb_t * p, struct policy_file *fp)
{
	size_t items;
	uint32_t buf[2];
	int new_rangetr = (p->policy_type == POLICY_KERN &&
			   p->policyvers >= POLICYDB_VERSION_RANGETRANS);
	struct rangetrans_write_args args;
	int rc;

	args.nel = 0;
	args.new_rangetr = new_rangetr;
	args.fp = fp;
	rc = hashtab_map(p->range_tr, rangetrans_count, &args);
	if (rc)
		return rc;

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

	return hashtab_map(p->range_tr, range_write_helper, &args);
}

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

static int avrule_write(policydb_t *p, 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->tclass);
		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;
	}

	if (avrule->specified & AVRULE_XPERMS) {
		size_t nel = ARRAY_SIZE(avrule->xperms->perms);
		uint32_t buf32[nel];
		uint8_t buf8;
		unsigned int i;

		if (p->policyvers < MOD_POLICYDB_VERSION_XPERMS_IOCTL) {
			ERR(fp->handle,
			    "module policy version %u does not support ioctl"
			    " extended permissions rules and one was specified",
			    p->policyvers);
			return POLICYDB_ERROR;
		}

		if (p->target_platform != SEPOL_TARGET_SELINUX) {
			ERR(fp->handle,
			    "Target platform %s does not support ioctl"
			    " extended permissions rules and one was specified",
			    policydb_target_strings[p->target_platform]);
			return POLICYDB_ERROR;
		}

		buf8 = avrule->xperms->specified;
		items = put_entry(&buf8, sizeof(uint8_t),1,fp);
		if (items != 1)
			return POLICYDB_ERROR;
		buf8 = avrule->xperms->driver;
		items = put_entry(&buf8, sizeof(uint8_t),1,fp);
		if (items != 1)
			return POLICYDB_ERROR;
		for (i = 0; i < nel; i++)
			buf32[i] = cpu_to_le32(avrule->xperms->perms[i]);
		items = put_entry(buf32, sizeof(uint32_t), nel, fp);
		if (items != nel)
			return POLICYDB_ERROR;
	}

	return POLICYDB_SUCCESS;
}

static int avrule_write_list(policydb_t *p, 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) {
		if (avrule_write(p, avrule, fp))
			return POLICYDB_ERROR;
		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)
				WARN(fp->handle, "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(p, 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;
	const 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)) {
			ERR(fp->handle, "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) {
		ERR(fp->handle, "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)) {
				WARN(fp->handle, "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)
				WARN(fp->handle, "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, fp))
				return POLICYDB_ERROR;
		} else {
			if (p->filename_trans)
				WARN(fp->handle, "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;
}
