diff mbox

[2/2] libsepol, checkpolicy: convert rangetrans and filenametrans to hashtabs

Message ID 1479921089-11519-3-git-send-email-sds@tycho.nsa.gov (mailing list archive)
State Not Applicable
Headers show

Commit Message

Stephen Smalley Nov. 23, 2016, 5:11 p.m. UTC
range transition and name-based type transition rules were originally
simple unordered lists.  They were converted to hashtabs in the kernel
by commit 2f3e82d694d3d7a2db019db1bb63385fbc1066f3 ("selinux: convert range
transition list to a hashtab") and by commit
2463c26d50adc282d19317013ba0ff473823ca47 ("SELinux: put name based
create rules in a hashtable"), but left unchanged in libsepol and
checkpolicy. Convert libsepol and checkpolicy to use the same hashtabs
as the kernel for the range transitions and name-based type transitions.

With this change and the preceding one, it is possible to directly compare
a policy file generated by libsepol/checkpolicy and the kernel-generated
/sys/fs/selinux/policy pseudo file after normalizing them both through
checkpolicy.  To do so, you can run the following sequence of commands:

checkpolicy -M -b /etc/selinux/targeted/policy/policy.30 -o policy.1
checkpolicy -M -b /sys/fs/selinux/policy -o policy.2
cmp policy.1 policy.2

Normalizing the two files via checkpolicy is still necessary to ensure
consistent ordering of the avtab entries.  There may still be potential
for other areas of difference, e.g. xperms entries may lack a well-defined
order.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
---
 checkpolicy/policy_define.c                |  57 +++---
 checkpolicy/test/dispol.c                  |  36 +++-
 libsepol/cil/src/cil_binary.c              |  79 +++++---
 libsepol/include/sepol/policydb/context.h  |  40 ++++
 libsepol/include/sepol/policydb/policydb.h |  18 +-
 libsepol/src/expand.c                      | 152 ++++++++-------
 libsepol/src/mls.c                         |  52 +++--
 libsepol/src/policydb.c                    | 293 +++++++++++++++++++----------
 libsepol/src/write.c                       | 184 ++++++++++++------
 9 files changed, 588 insertions(+), 323 deletions(-)
diff mbox

Patch

diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index 8b56a29..8647889 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -3220,11 +3220,12 @@  int define_filename_trans(void)
 	ebitmap_t e_tclasses;
 	ebitmap_node_t *snode, *tnode, *cnode;
 	filename_trans_t *ft;
+	filename_trans_datum_t *ftdatum;
 	filename_trans_rule_t *ftr;
 	type_datum_t *typdatum;
 	uint32_t otype;
 	unsigned int c, s, t;
-	int add;
+	int add, rc;
 
 	if (pass == 1) {
 		/* stype */
@@ -3308,40 +3309,44 @@  int define_filename_trans(void)
 			ebitmap_for_each_bit(&e_ttypes, tnode, t) {
 				if (!ebitmap_node_get_bit(tnode, t))
 					continue;
-	
-				for (ft = policydbp->filename_trans; ft; ft = ft->next) {
-					if (ft->stype == (s + 1) &&
-					    ft->ttype == (t + 1) &&
-					    ft->tclass == (c + 1) &&
-					    !strcmp(ft->name, name)) {
-						yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
-							 name, 
-							 policydbp->p_type_val_to_name[s],
-							 policydbp->p_type_val_to_name[t],
-							 policydbp->p_class_val_to_name[c]);
-						goto bad;
-					}
-				}
-	
-				ft = malloc(sizeof(*ft));
+
+				ft = calloc(1, sizeof(*ft));
 				if (!ft) {
 					yyerror("out of memory");
 					goto bad;
 				}
-				memset(ft, 0, sizeof(*ft));
-	
-				ft->next = policydbp->filename_trans;
-				policydbp->filename_trans = ft;
-	
+				ft->stype = s+1;
+				ft->ttype = t+1;
+				ft->tclass = c+1;
 				ft->name = strdup(name);
 				if (!ft->name) {
 					yyerror("out of memory");
 					goto bad;
 				}
-				ft->stype = s + 1;
-				ft->ttype = t + 1;
-				ft->tclass = c + 1;
-				ft->otype = otype;
+
+				ftdatum = hashtab_search(policydbp->filename_trans,
+							 (hashtab_key_t)ft);
+				if (ftdatum) {
+					yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
+						 name,
+						 policydbp->p_type_val_to_name[s],
+						 policydbp->p_type_val_to_name[t],
+						 policydbp->p_class_val_to_name[c]);
+					goto bad;
+				}
+
+				ftdatum = calloc(1, sizeof(*ftdatum));
+				if (!ftdatum) {
+					yyerror("out of memory");
+					goto bad;
+				}
+				rc = hashtab_insert(policydbp->filename_trans,
+						    (hashtab_key_t)ft,
+						    ftdatum);
+				if (rc) {
+					yyerror("out of memory");
+					goto bad;
+				}
 			}
 		}
 	
diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
index a78ce81..d91fafd 100644
--- a/checkpolicy/test/dispol.c
+++ b/checkpolicy/test/dispol.c
@@ -330,18 +330,38 @@  static void display_role_trans(policydb_t *p, FILE *fp)
 	}
 }
 
+struct filenametr_display_args {
+	policydb_t *p;
+	FILE *fp;
+};
+
+static int filenametr_display(hashtab_key_t key,
+			      hashtab_datum_t datum,
+			      void *ptr)
+{
+	struct filename_trans *ft = (struct filename_trans *)key;
+	struct filename_trans_datum *ftdatum = datum;
+	struct filenametr_display_args *args = ptr;
+	policydb_t *p = args->p;
+	FILE *fp = args->fp;
+
+	display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
+	display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
+	display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
+	display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, "");
+	fprintf(fp, " %s\n", ft->name);
+	return 0;
+}
+
+
 static void display_filename_trans(policydb_t *p, FILE *fp)
 {
-	filename_trans_t *ft;
+	struct filenametr_display_args args;
 
 	fprintf(fp, "filename_trans rules:\n");
-	for (ft = p->filename_trans; ft; ft = ft->next) {
-		display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
-		display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
-		display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
-		display_id(p, fp, SYM_TYPES, ft->otype - 1, "");
-		fprintf(fp, " %s\n", ft->name);
-	}
+	args.p = p;
+	args.fp = fp;
+	hashtab_map(p->filename_trans, filenametr_display, &args);
 }
 
 int menu(void)
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index a813201..d33981b 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -1131,13 +1131,13 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 	class_datum_t *sepol_obj = NULL;
 	struct cil_list *class_list;
 	type_datum_t *sepol_result = NULL;
-	filename_trans_t *new = NULL;
+	filename_trans_t *newkey = NULL;
+	filename_trans_datum_t *newdatum = NULL, *otype = NULL;
 	ebitmap_t src_bitmap, tgt_bitmap;
 	ebitmap_node_t *node1, *node2;
 	unsigned int i, j;
 	struct cil_list_item *c;
 	char *name = DATUM(typetrans->name)->name;
-	uint32_t *otype = NULL;
 
 	if (name == CIL_KEY_STAR) {
 		struct cil_type_rule trans;
@@ -1177,20 +1177,20 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 				rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj);
 				if (rc != SEPOL_OK) goto exit;
 
-				new = cil_malloc(sizeof(*new));
-				memset(new, 0, sizeof(*new));
-				new->stype = sepol_src->s.value;
-				new->ttype = sepol_tgt->s.value;
-				new->tclass = sepol_obj->s.value;
-				new->otype = sepol_result->s.value;
-				new->name = cil_strdup(name);
+				newkey = cil_calloc(1, sizeof(*newkey));
+				newdatum = cil_calloc(1, sizeof(*newdatum));
+				newkey->stype = sepol_src->s.value;
+				newkey->ttype = sepol_tgt->s.value;
+				newkey->tclass = sepol_obj->s.value;
+				newkey->name = cil_strdup(name);
+				newdatum->otype = sepol_result->s.value;
 
-				rc = hashtab_insert(filename_trans_table, (hashtab_key_t)new, &(new->otype));
+				rc = hashtab_insert(filename_trans_table, (hashtab_key_t)newkey, newdatum);
 				if (rc != SEPOL_OK) {
 					if (rc == SEPOL_EEXIST) {
 						add = CIL_FALSE;
-						otype = hashtab_search(filename_trans_table, (hashtab_key_t)new);
-						if (new->otype != *otype) {
+						otype = hashtab_search(filename_trans_table, (hashtab_key_t)newkey);
+						if (newdatum->otype != otype->otype) {
 							cil_log(CIL_ERR, "Conflicting name type transition rules\n");
 						} else {
 							rc = SEPOL_OK;
@@ -1201,11 +1201,17 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 				}
 
 				if (add == CIL_TRUE) {
-					new->next = pdb->filename_trans;
-					pdb->filename_trans = new;
+					rc = hashtab_insert(pdb->filename_trans,
+							    (hashtab_key_t)newkey,
+							    newdatum);
+					if (rc != SEPOL_OK) {
+						cil_log(CIL_ERR, "Out of memory\n");
+						goto exit;
+					}
 				} else {
-					free(new->name);
-					free(new);
+					free(newkey->name);
+					free(newkey);
+					free(newdatum);
 					if (rc != SEPOL_OK) {
 						goto exit;
 					}
@@ -2943,7 +2949,8 @@  int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st
 	type_datum_t *sepol_tgt = NULL;
 	class_datum_t *sepol_class = NULL;
 	struct cil_list *class_list;
-	range_trans_t *new;
+	range_trans_t *newkey = NULL;
+	struct mls_range *newdatum = NULL;
 	ebitmap_t src_bitmap, tgt_bitmap;
 	ebitmap_node_t *node1, *node2;
 	unsigned int i, j;
@@ -2975,24 +2982,25 @@  int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st
 				rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_class);
 				if (rc != SEPOL_OK) goto exit;
 
-				new = cil_malloc(sizeof(*new));
-				memset(new, 0, sizeof(range_trans_t));
-				new->source_type = sepol_src->s.value;
-				new->target_type = sepol_tgt->s.value;
-				new->target_class = sepol_class->s.value;
-				rc = __cil_levelrange_to_mls_range(pdb, rangetrans->range, &new->target_range);
+				newkey = cil_calloc(1, sizeof(*newkey));
+				newdatum = cil_calloc(1, sizeof(*newdatum));
+				newkey->source_type = sepol_src->s.value;
+				newkey->target_type = sepol_tgt->s.value;
+				newkey->target_class = sepol_class->s.value;
+				rc = __cil_levelrange_to_mls_range(pdb, rangetrans->range, newdatum);
 				if (rc != SEPOL_OK) {
-					free(new);
+					free(newkey);
+					free(newdatum);
 					goto exit;
 				}
 
 				rc = SEPOL_OK;
-				rc = hashtab_insert(range_trans_table, (hashtab_key_t)new, &(new->target_range));
+				rc = hashtab_insert(range_trans_table, (hashtab_key_t)newkey, newdatum);
 				if (rc != SEPOL_OK) {
 					if (rc == SEPOL_EEXIST) {
 						add = CIL_FALSE;
-						o_range = hashtab_search(range_trans_table, (hashtab_key_t)new);
-						if (!mls_range_eq(&new->target_range, o_range)) {
+						o_range = hashtab_search(range_trans_table, (hashtab_key_t)newkey);
+						if (!mls_range_eq(newdatum, o_range)) {
 							cil_log(CIL_ERR, "Conflicting Range transition rules\n");
 						} else {
 							rc = SEPOL_OK;
@@ -3003,11 +3011,20 @@  int cil_rangetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, st
 				}
 
 				if (add == CIL_TRUE) {
-					new->next = pdb->range_tr;
-					pdb->range_tr = new;
+					rc = hashtab_insert(pdb->range_tr,
+							    (hashtab_key_t)newkey,
+							    newdatum);
+					if (rc != SEPOL_OK) {
+						mls_range_destroy(newdatum);
+						free(newdatum);
+						free(newkey);
+						cil_log(CIL_ERR, "Out of memory\n");
+						goto exit;
+					}
 				} else {
-					mls_range_destroy(&new->target_range);
-					free(new);
+					mls_range_destroy(newdatum);
+					free(newdatum);
+					free(newkey);
 					if (rc != SEPOL_OK) {
 						goto exit;
 					}
diff --git a/libsepol/include/sepol/policydb/context.h b/libsepol/include/sepol/policydb/context.h
index dbb7c3e..c844c37 100644
--- a/libsepol/include/sepol/policydb/context.h
+++ b/libsepol/include/sepol/policydb/context.h
@@ -50,6 +50,46 @@  static inline int mls_context_cpy(context_struct_t * dst,
 	return 0;
 }
 
+/*
+ * Sets both levels in the MLS range of 'dst' to the low level of 'src'.
+ */
+static inline int mls_context_cpy_low(context_struct_t *dst, context_struct_t *src)
+{
+	int rc;
+
+	dst->range.level[0].sens = src->range.level[0].sens;
+	rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
+	if (rc)
+		goto out;
+
+	dst->range.level[1].sens = src->range.level[0].sens;
+	rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat);
+	if (rc)
+		ebitmap_destroy(&dst->range.level[0].cat);
+out:
+	return rc;
+}
+
+/*
+ * Sets both levels in the MLS range of 'dst' to the high level of 'src'.
+ */
+static inline int mls_context_cpy_high(context_struct_t *dst, context_struct_t *src)
+{
+	int rc;
+
+	dst->range.level[0].sens = src->range.level[1].sens;
+	rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat);
+	if (rc)
+		goto out;
+
+	dst->range.level[1].sens = src->range.level[1].sens;
+	rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat);
+	if (rc)
+		ebitmap_destroy(&dst->range.level[0].cat);
+out:
+	return rc;
+}
+
 static inline int mls_context_cmp(context_struct_t * c1, context_struct_t * c2)
 {
 	return (mls_level_eq(&c1->range.level[0], &c2->range.level[0]) &&
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index 77e46fb..31cdd76 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -162,10 +162,12 @@  typedef struct filename_trans {
 	uint32_t ttype;
 	uint32_t tclass;
 	char *name;
-	uint32_t otype;
-	struct filename_trans *next;
 } filename_trans_t;
 
+typedef struct filename_trans_datum {
+	uint32_t otype;		/* expected of new object */
+} filename_trans_datum_t;
+
 /* Type attributes */
 typedef struct type_datum {
 	symtab_datum_t s;
@@ -218,8 +220,6 @@  typedef struct range_trans {
 	uint32_t source_type;
 	uint32_t target_type;
 	uint32_t target_class;
-	mls_range_t target_range;
-	struct range_trans *next;
 } range_trans_t;
 
 /* Boolean data type */
@@ -555,9 +555,6 @@  typedef struct policydb {
 	/* role transitions */
 	role_trans_t *role_tr;
 
-	/* type transition rules with a 'name' component */
-	filename_trans_t *filename_trans;
-
 	/* role allows */
 	role_allow_t *role_allow;
 
@@ -570,8 +567,11 @@  typedef struct policydb {
 	   fixed labeling behavior. */
 	genfs_t *genfs;
 
-	/* range transitions */
-	range_trans_t *range_tr;
+	/* range transitions table (range_trans_key -> mls_range) */
+	hashtab_t range_tr;
+
+	/* file transitions with the last path component */
+	hashtab_t filename_trans;
 
 	ebitmap_t *type_attr_map;
 
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index 32df6f8..c5e0f3c 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -1385,10 +1385,12 @@  static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules)
 static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
 {
 	unsigned int i, j;
-	filename_trans_t *new_trans, *cur_trans;
+	filename_trans_t key, *new_trans;
+	filename_trans_datum_t *otype;
 	filename_trans_rule_t *cur_rule;
 	ebitmap_t stypes, ttypes;
 	ebitmap_node_t *snode, *tnode;
+	int rc;
 
 	cur_rule = rules;
 	while (cur_rule) {
@@ -1418,40 +1420,32 @@  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
 				if (!ebitmap_node_get_bit(tnode, j))
 					continue;
 
-				cur_trans = state->out->filename_trans;
-				while (cur_trans) {
-					if ((cur_trans->stype == i + 1) &&
-					    (cur_trans->ttype == j + 1) &&
-					    (cur_trans->tclass == cur_rule->tclass) &&
-					    (!strcmp(cur_trans->name, cur_rule->name))) {
-						/* duplicate rule, who cares */
-						if (cur_trans->otype == mapped_otype)
-							break;
-
-						ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
-						    state->out->p_type_val_to_name[i],
-						    state->out->p_type_val_to_name[j],
-						    state->out->p_class_val_to_name[cur_trans->tclass - 1],
-						    cur_trans->name,
-						    state->out->p_type_val_to_name[cur_trans->otype - 1],
-						    state->out->p_type_val_to_name[mapped_otype - 1]);
+				key.stype = i + 1;
+				key.ttype = j + 1;
+				key.tclass = cur_rule->tclass;
+				key.name = cur_rule->name;
+				otype = hashtab_search(state->out->filename_trans,
+						       (hashtab_key_t) &key);
+				if (otype) {
+					/* duplicate rule, ignore */
+					if (otype->otype == mapped_otype)
+						continue;
 
-						return -1;
-					}
-					cur_trans = cur_trans->next;
+					ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
+					    state->out->p_type_val_to_name[i],
+					    state->out->p_type_val_to_name[j],
+					    state->out->p_class_val_to_name[cur_rule->tclass - 1],
+					    cur_rule->name,
+					    state->out->p_type_val_to_name[otype->otype - 1],
+					    state->out->p_type_val_to_name[mapped_otype - 1]);
+					return -1;
 				}
-				/* duplicate rule, who cares */
-				if (cur_trans)
-					continue;
 
-				new_trans = malloc(sizeof(*new_trans));
+				new_trans = calloc(1, sizeof(*new_trans));
 				if (!new_trans) {
 					ERR(state->handle, "Out of memory!");
 					return -1;
 				}
-				memset(new_trans, 0, sizeof(*new_trans));
-				new_trans->next = state->out->filename_trans;
-				state->out->filename_trans = new_trans;
 
 				new_trans->name = strdup(cur_rule->name);
 				if (!new_trans->name) {
@@ -1461,7 +1455,21 @@  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
 				new_trans->stype = i + 1;
 				new_trans->ttype = j + 1;
 				new_trans->tclass = cur_rule->tclass;
-				new_trans->otype = mapped_otype;
+
+				otype = calloc(1, sizeof(*otype));
+				if (!otype) {
+					ERR(state->handle, "Out of memory!");
+					return -1;
+				}
+				otype->otype = mapped_otype;
+
+				rc = hashtab_insert(state->out->filename_trans,
+						    (hashtab_key_t)new_trans,
+						    otype);
+				if (rc) {
+					ERR(state->handle, "Out of memory!");
+					return -1;
+				}
 			}
 		}
 
@@ -1477,63 +1485,67 @@  static int exp_rangetr_helper(uint32_t stype, uint32_t ttype, uint32_t tclass,
 			      mls_semantic_range_t * trange,
 			      expand_state_t * state)
 {
-	range_trans_t *rt, *check_rt = state->out->range_tr;
-	mls_range_t exp_range;
+	range_trans_t *rt = NULL, key;
+	mls_range_t *r, *exp_range = NULL;
 	int rc = -1;
 
-	if (mls_semantic_range_expand(trange, &exp_range, state->out,
+	exp_range = calloc(1, sizeof(*exp_range));
+	if (!exp_range) {
+		ERR(state->handle, "Out of memory!");
+		return -1;
+	}
+
+	if (mls_semantic_range_expand(trange, exp_range, state->out,
 				      state->handle))
-		goto out;
+		goto err;
 
 	/* check for duplicates/conflicts */
-	while (check_rt) {
-		if ((check_rt->source_type == stype) &&
-		    (check_rt->target_type == ttype) &&
-		    (check_rt->target_class == tclass)) {
-			if (mls_range_eq(&check_rt->target_range, &exp_range)) {
-				/* duplicate */
-				break;
-			} else {
-				/* conflict */
-				ERR(state->handle,
-				    "Conflicting range trans rule %s %s : %s",
-				    state->out->p_type_val_to_name[stype - 1],
-				    state->out->p_type_val_to_name[ttype - 1],
-				    state->out->p_class_val_to_name[tclass -
-								    1]);
-				goto out;
-			}
-		}
-		check_rt = check_rt->next;
-	}
-	if (check_rt) {
-		/* this is a dup - skip */
-		rc = 0;
-		goto out;
+	key.source_type = stype;
+	key.target_type = ttype;
+	key.target_class = tclass;
+	r = hashtab_search(state->out->range_tr, (hashtab_key_t) &key);
+	if (r) {
+		if (mls_range_eq(r, exp_range)) {
+			/* duplicate, ignore */
+			mls_range_destroy(exp_range);
+			free(exp_range);
+			return 0;
+		}
+
+		/* conflict */
+		ERR(state->handle,
+		    "Conflicting range trans rule %s %s : %s",
+		    state->out->p_type_val_to_name[stype - 1],
+		    state->out->p_type_val_to_name[ttype - 1],
+		    state->out->p_class_val_to_name[tclass - 1]);
+		goto err;
 	}
 
-	rt = (range_trans_t *) calloc(1, sizeof(range_trans_t));
+	rt = calloc(1, sizeof(*rt));
 	if (!rt) {
 		ERR(state->handle, "Out of memory!");
-		goto out;
+		goto err;
 	}
-
-	rt->next = state->out->range_tr;
-	state->out->range_tr = rt;
-
 	rt->source_type = stype;
 	rt->target_type = ttype;
 	rt->target_class = tclass;
-	if (mls_range_cpy(&rt->target_range, &exp_range)) {
+
+	rc = hashtab_insert(state->out->range_tr, (hashtab_key_t) rt,
+			    exp_range);
+	if (rc) {
 		ERR(state->handle, "Out of memory!");
-		goto out;
-	}
+		goto err;
 
-	rc = 0;
+	}
 
-      out:
-	mls_range_destroy(&exp_range);
-	return rc;
+	return 0;
+err:
+	free(rt);
+	if (exp_range) {
+		mls_range_destroy(exp_range);
+		free(exp_range);
+	}
+	return -1;
 }
 
 static int expand_range_trans(expand_state_t * state,
diff --git a/libsepol/src/mls.c b/libsepol/src/mls.c
index 8047d91..be85475 100644
--- a/libsepol/src/mls.c
+++ b/libsepol/src/mls.c
@@ -610,22 +610,45 @@  int mls_compute_sid(policydb_t * policydb,
 		    sepol_security_class_t tclass,
 		    uint32_t specified, context_struct_t * newcontext)
 {
-	range_trans_t *rtr;
+	range_trans_t rtr;
+	struct mls_range *r;
+	struct class_datum *cladatum;
+	int default_range = 0;
+
 	if (!policydb->mls)
 		return 0;
 
 	switch (specified) {
 	case AVTAB_TRANSITION:
 		/* Look for a range transition rule. */
-		for (rtr = policydb->range_tr; rtr; rtr = rtr->next) {
-			if (rtr->source_type == scontext->type &&
-			    rtr->target_type == tcontext->type &&
-			    rtr->target_class == tclass) {
-				/* Set the range from the rule */
-				return mls_range_set(newcontext,
-						     &rtr->target_range);
-			}
+		rtr.source_type = scontext->type;
+		rtr.target_type = tcontext->type;
+		rtr.target_class = tclass;
+		r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr);
+		if (r)
+			return mls_range_set(newcontext, r);
+
+		if (tclass && tclass <= policydb->p_classes.nprim) {
+			cladatum = policydb->class_val_to_struct[tclass - 1];
+			if (cladatum)
+				default_range = cladatum->default_range;
+		}
+
+		switch (default_range) {
+		case DEFAULT_SOURCE_LOW:
+			return mls_context_cpy_low(newcontext, scontext);
+		case DEFAULT_SOURCE_HIGH:
+			return mls_context_cpy_high(newcontext, scontext);
+		case DEFAULT_SOURCE_LOW_HIGH:
+			return mls_context_cpy(newcontext, scontext);
+		case DEFAULT_TARGET_LOW:
+			return mls_context_cpy_low(newcontext, tcontext);
+		case DEFAULT_TARGET_HIGH:
+			return mls_context_cpy_high(newcontext, tcontext);
+		case DEFAULT_TARGET_LOW_HIGH:
+			return mls_context_cpy(newcontext, tcontext);
 		}
+
 		/* Fallthrough */
 	case AVTAB_CHANGE:
 		if (tclass == SECCLASS_PROCESS)
@@ -635,15 +658,8 @@  int mls_compute_sid(policydb_t * policydb,
 			/* Use the process effective MLS attributes. */
 			return mls_scopy_context(newcontext, scontext);
 	case AVTAB_MEMBER:
-		/* Only polyinstantiate the MLS attributes if
-		   the type is being polyinstantiated */
-		if (newcontext->type != tcontext->type) {
-			/* Use the process effective MLS attributes. */
-			return mls_scopy_context(newcontext, scontext);
-		} else {
-			/* Use the related object MLS attributes. */
-			return mls_copy_context(newcontext, tcontext);
-		}
+		/* Use the process effective MLS attributes. */
+		return mls_context_cpy_low(newcontext, scontext);
 	default:
 		return -EINVAL;
 	}
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index d1019e4..9d15fd1 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -721,6 +721,77 @@  static int roles_init(policydb_t * p)
 	goto out;
 }
 
+static inline unsigned long
+partial_name_hash(unsigned long c, unsigned long prevhash)
+{
+	return (prevhash + (c << 4) + (c >> 4)) * 11;
+}
+
+static unsigned int filenametr_hash(hashtab_t h, hashtab_key_t k)
+{
+	const struct filename_trans *ft = (const struct filename_trans *)k;
+	unsigned long hash;
+	unsigned int byte_num;
+	unsigned char focus;
+
+	hash = ft->stype ^ ft->ttype ^ ft->tclass;
+
+	byte_num = 0;
+	while ((focus = ft->name[byte_num++]))
+		hash = partial_name_hash(focus, hash);
+	return hash & (h->size - 1);
+}
+
+static int filenametr_cmp(hashtab_t h __attribute__ ((unused)),
+			  hashtab_key_t k1, hashtab_key_t k2)
+{
+	const struct filename_trans *ft1 = (const struct filename_trans *)k1;
+	const struct filename_trans *ft2 = (const struct filename_trans *)k2;
+	int v;
+
+	v = ft1->stype - ft2->stype;
+	if (v)
+		return v;
+
+	v = ft1->ttype - ft2->ttype;
+	if (v)
+		return v;
+
+	v = ft1->tclass - ft2->tclass;
+	if (v)
+		return v;
+
+	return strcmp(ft1->name, ft2->name);
+
+}
+
+static unsigned int rangetr_hash(hashtab_t h, hashtab_key_t k)
+{
+	const struct range_trans *key = (const struct range_trans *)k;
+	return (key->source_type + (key->target_type << 3) +
+		(key->target_class << 5)) & (h->size - 1);
+}
+
+static int rangetr_cmp(hashtab_t h __attribute__ ((unused)),
+		       hashtab_key_t k1, hashtab_key_t k2)
+{
+	const struct range_trans *key1 = (const struct range_trans *)k1;
+	const struct range_trans *key2 = (const struct range_trans *)k2;
+	int v;
+
+	v = key1->source_type - key2->source_type;
+	if (v)
+		return v;
+
+	v = key1->target_type - key2->target_type;
+	if (v)
+		return v;
+
+	v = key1->target_class - key2->target_class;
+
+	return v;
+}
+
 /*
  * Initialize a policy database structure.
  */
@@ -730,50 +801,63 @@  int policydb_init(policydb_t * p)
 
 	memset(p, 0, sizeof(policydb_t));
 
-	ebitmap_init(&p->policycaps);
-
-	ebitmap_init(&p->permissive_map);
-
 	for (i = 0; i < SYM_NUM; i++) {
 		p->sym_val_to_name[i] = NULL;
 		rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
 		if (rc)
-			goto out_free_symtab;
+			goto out;
 	}
 
 	/* initialize the module stuff */
 	for (i = 0; i < SYM_NUM; i++) {
 		if (symtab_init(&p->scope[i], symtab_sizes[i])) {
-			goto out_free_symtab;
+			goto out;
 		}
 	}
 	if ((p->global = avrule_block_create()) == NULL ||
 	    (p->global->branch_list = avrule_decl_create(1)) == NULL) {
-		goto out_free_symtab;
+		goto out;
 	}
 	p->decl_val_to_struct = NULL;
 
 	rc = avtab_init(&p->te_avtab);
 	if (rc)
-		goto out_free_symtab;
+		goto out;
 
 	rc = roles_init(p);
 	if (rc)
-		goto out_free_symtab;
+		goto out;
+
+	p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10));
+	if (!p->filename_trans) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
+	if (!p->range_tr) {
+		rc = -ENOMEM;
+		goto out;
+	}
 
 	rc = cond_policydb_init(p);
 	if (rc)
-		goto out_free_symtab;
-      out:
-	return rc;
+		goto out;
+
+	ebitmap_init(&p->policycaps);
+	ebitmap_init(&p->permissive_map);
 
-      out_free_symtab:
+	return 0;
+
+out:
+	hashtab_destroy(p->filename_trans);
+	hashtab_destroy(p->range_tr);
 	for (i = 0; i < SYM_NUM; i++) {
 		hashtab_destroy(p->symtab[i].table);
 		hashtab_destroy(p->scope[i].table);
 	}
 	avrule_block_list_destroy(p->global);
-	goto out;
+	return 0;
 }
 
 int policydb_role_cache(hashtab_key_t key
@@ -1242,6 +1326,27 @@  static int (*destroy_f[SYM_NUM]) (hashtab_key_t key, hashtab_datum_t datum,
 common_destroy, class_destroy, role_destroy, type_destroy, user_destroy,
 	    cond_destroy_bool, sens_destroy, cat_destroy,};
 
+static int filenametr_destroy(hashtab_key_t key, hashtab_datum_t datum,
+			      void *p __attribute__ ((unused)))
+{
+	struct filename_trans *ft = (struct filename_trans *)key;
+	free(ft->name);
+	free(key);
+	free(datum);
+	return 0;
+}
+
+static int range_tr_destroy(hashtab_key_t key, hashtab_datum_t datum,
+			    void *p __attribute__ ((unused)))
+{
+	struct mls_range *rt = (struct mls_range *)datum;
+	free(key);
+	ebitmap_destroy(&rt->level[0].cat);
+	ebitmap_destroy(&rt->level[1].cat);
+	free(datum);
+	return 0;
+}
+
 void ocontext_selinux_free(ocontext_t **ocontexts)
 {
 	ocontext_t *c, *ctmp;
@@ -1291,8 +1396,6 @@  void policydb_destroy(policydb_t * p)
 	unsigned int i;
 	role_allow_t *ra, *lra = NULL;
 	role_trans_t *tr, *ltr = NULL;
-	range_trans_t *rt, *lrt = NULL;
-	filename_trans_t *ft, *nft;
 
 	if (!p)
 		return;
@@ -1358,14 +1461,6 @@  void policydb_destroy(policydb_t * p)
 	if (ltr)
 		free(ltr);
 
-	ft = p->filename_trans;
-	while (ft) {
-		nft = ft->next;
-		free(ft->name);
-		free(ft);
-		ft = nft;
-	}
-
 	for (ra = p->role_allow; ra; ra = ra->next) {
 		if (lra)
 			free(lra);
@@ -1374,19 +1469,11 @@  void policydb_destroy(policydb_t * p)
 	if (lra)
 		free(lra);
 
-	for (rt = p->range_tr; rt; rt = rt->next) {
-		if (lrt) {
-			ebitmap_destroy(&lrt->target_range.level[0].cat);
-			ebitmap_destroy(&lrt->target_range.level[1].cat);
-			free(lrt);
-		}
-		lrt = rt;
-	}
-	if (lrt) {
-		ebitmap_destroy(&lrt->target_range.level[0].cat);
-		ebitmap_destroy(&lrt->target_range.level[1].cat);
-		free(lrt);
-	}
+	hashtab_map(p->filename_trans, filenametr_destroy, NULL);
+	hashtab_destroy(p->filename_trans);
+
+	hashtab_map(p->range_tr, range_tr_destroy, NULL);
+	hashtab_destroy(p->range_tr);
 
 	if (p->type_attr_map) {
 		for (i = 0; i < p->p_types.nprim; i++) {
@@ -2428,11 +2515,12 @@  int role_allow_read(role_allow_t ** r, struct policy_file *fp)
 	return 0;
 }
 
-int filename_trans_read(filename_trans_t **t, struct policy_file *fp)
+int filename_trans_read(policydb_t *p, struct policy_file *fp)
 {
 	unsigned int i;
 	uint32_t buf[4], nel, len;
-	filename_trans_t *ft, *lft;
+	filename_trans_t *ft;
+	filename_trans_datum_t *otype;
 	int rc;
 	char *name;
 
@@ -2441,43 +2529,54 @@  int filename_trans_read(filename_trans_t **t, struct policy_file *fp)
 		return -1;
 	nel = le32_to_cpu(buf[0]);
 
-	lft = NULL;
 	for (i = 0; i < nel; i++) {
-		ft = calloc(1, sizeof(struct filename_trans));
+		ft = NULL;
+		otype = NULL;
+		name = NULL;
+
+		ft = calloc(1, sizeof(*ft));
 		if (!ft)
-			return -1;
-		if (lft)
-			lft->next = ft;
-		else
-			*t = ft;
-		lft = ft;
+			goto err;
+		otype = calloc(1, sizeof(*otype));
+		if (!ft)
+			goto err;
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0)
-			return -1;
+			goto err;
 		len = le32_to_cpu(buf[0]);
 		if (zero_or_saturated(len))
-			return -1;
+			goto err;
 
 		name = calloc(len + 1, sizeof(*name));
 		if (!name)
-			return -1;
+			goto err;
 
 		ft->name = name;
 
 		rc = next_entry(name, fp, len);
 		if (rc < 0)
-			return -1;
+			goto err;
 
 		rc = next_entry(buf, fp, sizeof(uint32_t) * 4);
 		if (rc < 0)
-			return -1;
+			goto err;
 
 		ft->stype = le32_to_cpu(buf[0]);
 		ft->ttype = le32_to_cpu(buf[1]);
 		ft->tclass = le32_to_cpu(buf[2]);
-		ft->otype = le32_to_cpu(buf[3]);
+		otype->otype = le32_to_cpu(buf[3]);
+
+		rc = hashtab_insert(p->filename_trans, (hashtab_key_t) ft,
+				    otype);
+		if (rc)
+			goto err;
 	}
 	return 0;
+err:
+	free(ft);
+	free(otype);
+	free(name);
+	return -1;
 }
 
 static int ocontext_read_xen(struct policydb_compat_info *info,
@@ -3129,8 +3228,9 @@  static avrule_t *avrule_read(policydb_t * p
 static int range_read(policydb_t * p, struct policy_file *fp)
 {
 	uint32_t buf[2], nel;
-	range_trans_t *rt, *lrt;
-	range_trans_rule_t *rtr, *lrtr = NULL;
+	range_trans_t *rt = NULL;
+	struct mls_range *r = NULL;
+	range_trans_rule_t *rtr = NULL, *lrtr = NULL;
 	unsigned int i;
 	int new_rangetr = (p->policy_type == POLICY_KERN &&
 			   p->policyvers >= POLICYDB_VERSION_RANGETRANS);
@@ -3140,44 +3240,41 @@  static int range_read(policydb_t * p, struct policy_file *fp)
 	if (rc < 0)
 		return -1;
 	nel = le32_to_cpu(buf[0]);
-	lrt = NULL;
 	for (i = 0; i < nel; i++) {
 		rt = calloc(1, sizeof(range_trans_t));
 		if (!rt)
 			return -1;
-		if (lrt)
-			lrt->next = rt;
-		else
-			p->range_tr = rt;
 		rc = next_entry(buf, fp, (sizeof(uint32_t) * 2));
 		if (rc < 0)
-			return -1;
+			goto err;
 		rt->source_type = le32_to_cpu(buf[0]);
 		rt->target_type = le32_to_cpu(buf[1]);
 		if (new_rangetr) {
 			rc = next_entry(buf, fp, (sizeof(uint32_t)));
 			if (rc < 0)
-				return -1;
+				goto err;
 			rt->target_class = le32_to_cpu(buf[0]);
 		} else
 			rt->target_class = SECCLASS_PROCESS;
-		if (mls_read_range_helper(&rt->target_range, fp))
-			return -1;
-		lrt = rt;
-	}
-
-	/* if this is a kernel policy, we are done - otherwise we need to
-	 * convert these structs to range_trans_rule_ts */
-	if (p->policy_type == POLICY_KERN)
-		return 0;
+		r = calloc(1, sizeof(*r));
+		if (!r)
+			goto err;
+		if (mls_read_range_helper(r, fp))
+			goto err;
+
+		if (p->policy_type == POLICY_KERN) {
+			rc = hashtab_insert(p->range_tr, (hashtab_key_t)rt, r);
+			if (rc)
+				goto err;
+			rt = NULL;
+			r = NULL;
+			continue;
+		}
 
-	/* create range_trans_rules_ts that correspond to the range_trans_ts
-	 * that were just read in from an older policy */
-	for (rt = p->range_tr; rt; rt = rt->next) {
+		/* Module policy: convert to range_trans_rule and discard. */
 		rtr = malloc(sizeof(range_trans_rule_t));
-		if (!rtr) {
-			return -1;
-		}
+		if (!rtr)
+			goto err;
 		range_trans_rule_init(rtr);
 
 		if (lrtr)
@@ -3186,38 +3283,36 @@  static int range_read(policydb_t * p, struct policy_file *fp)
 			p->global->enabled->range_tr_rules = rtr;
 
 		if (ebitmap_set_bit(&rtr->stypes.types, rt->source_type - 1, 1))
-			return -1;
+			goto err;
 
 		if (ebitmap_set_bit(&rtr->ttypes.types, rt->target_type - 1, 1))
-			return -1;
+			goto err;
 
 		if (ebitmap_set_bit(&rtr->tclasses, rt->target_class - 1, 1))
-			return -1;
+			goto err;
 
-		if (mls_range_to_semantic(&rt->target_range, &rtr->trange))
-			return -1;
+		if (mls_range_to_semantic(r, &rtr->trange))
+			goto err;
 
+		free(rt);
+		rt = NULL;
+		free(r);
+		r = NULL;
 		lrtr = rtr;
 	}
 
-	/* now destroy the range_trans_ts */
-	lrt = NULL;
-	for (rt = p->range_tr; rt; rt = rt->next) {
-		if (lrt) {
-			ebitmap_destroy(&lrt->target_range.level[0].cat);
-			ebitmap_destroy(&lrt->target_range.level[1].cat);
-			free(lrt);
-		}
-		lrt = rt;
+	return 0;
+err:
+	free(rt);
+	if (r) {
+		mls_range_destroy(r);
+		free(r);
 	}
-	if (lrt) {
-		ebitmap_destroy(&lrt->target_range.level[0].cat);
-		ebitmap_destroy(&lrt->target_range.level[1].cat);
-		free(lrt);
+	if (rtr) {
+		range_trans_rule_destroy(rtr);
+		free(rtr);
 	}
-	p->range_tr = NULL;
-
-	return 0;
+	return -1;
 }
 
 int avrule_read_list(policydb_t * p, avrule_t ** avrules,
@@ -3908,7 +4003,7 @@  int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 		if (role_allow_read(&p->role_allow, fp))
 			goto bad;
 		if (r_policyvers >= POLICYDB_VERSION_FILENAME_TRANS &&
-		    filename_trans_read(&p->filename_trans, fp))
+		    filename_trans_read(p, fp))
 			goto bad;
 	} else {
 		/* first read the AV rule blocks, then the scope tables */
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index fbc6dad..190424f 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -563,40 +563,55 @@  static int role_allow_write(role_allow_t * r, struct policy_file *fp)
 	return POLICYDB_SUCCESS;
 }
 
-static int filename_trans_write(filename_trans_t * r, struct policy_file *fp)
+static int filename_write_helper(hashtab_key_t key, void *data, void *ptr)
 {
-	filename_trans_t *ft;
 	uint32_t buf[4];
-	size_t nel, items, len;
+	size_t items, len;
+	struct filename_trans *ft = (struct filename_trans *)key;
+	struct filename_trans_datum *otype = data;
+	void *fp = ptr;
 
-	nel = 0;
-	for (ft = r; ft; ft = ft->next)
-		nel++;
-	buf[0] = cpu_to_le32(nel);
+	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;
-	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;
+	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;
-	}
+	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 POLICYDB_SUCCESS;
+	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)
@@ -1512,51 +1527,96 @@  static int genfs_write(policydb_t * p, struct policy_file *fp)
 	return POLICYDB_SUCCESS;
 }
 
+
+struct rangetrans_count_args {
+	size_t nel;
+	int new_rangetr;
+};
+
+static int rangetrans_count(hashtab_key_t key,
+			    void *data __attribute__ ((unused)),
+			    void *ptr)
+{
+	struct range_trans *rt = (struct range_trans *)key;
+	struct rangetrans_count_args *count = 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 (count->new_rangetr || rt->target_class == SECCLASS_PROCESS)
+		count->nel++;
+	return 0;
+}
+
+struct rangetrans_write_args {
+	struct policy_data pd;
+	int new_rangetr;
+};
+
+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->pd.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 nel, items;
-	struct range_trans *rt;
+	size_t items;
 	uint32_t buf[2];
 	int new_rangetr = (p->policy_type == POLICY_KERN &&
 			   p->policyvers >= POLICYDB_VERSION_RANGETRANS);
-	int warning_issued = 0;
+	struct rangetrans_count_args cargs;
+	struct rangetrans_write_args wargs;
+	int rc;
 
-	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);
+	cargs.nel = 0;
+	cargs.new_rangetr = new_rangetr;
+	rc = hashtab_map(p->range_tr, rangetrans_count, &cargs);
+	if (rc)
+		return rc;
+
+	buf[0] = cpu_to_le32(cargs.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)
-				WARN(fp->handle, "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;
+
+	wargs.pd.p = p;
+	wargs.pd.fp = fp;
+	wargs.new_rangetr = new_rangetr;
+	return hashtab_map(p->range_tr, range_write_helper, &wargs);
 }
 
 /************** module writing functions below **************/
@@ -2136,7 +2196,7 @@  int policydb_write(policydb_t * p, struct policy_file *fp)
 		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))
+			if (filename_trans_write(p, fp))
 				return POLICYDB_ERROR;
 		} else {
 			if (p->filename_trans)