diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
index 6e8ed833..2485142d 100644
--- a/checkpolicy/checkpolicy.c
+++ b/checkpolicy/checkpolicy.c
@@ -611,15 +611,6 @@ int main(int argc, char **argv)
 		parse_policy.mls = mlspol;
 		parse_policy.handle_unknown = handle_unknown;
 
-		/*
-		 * Init and alloc te_avtab for filename transition duplicate
-		 * checking
-		 */
-		if (avtab_init(&parse_policy.te_avtab))
-			exit(1);
-		if (avtab_alloc(&parse_policy.te_avtab, 1 << 11))
-			exit(1);
-
 		policydbp = &parse_policy;
 
 		if (read_source_policy(policydbp, file, "checkpolicy") < 0)
diff --git a/checkpolicy/module_compiler.c b/checkpolicy/module_compiler.c
index 3188af89..5fe1729a 100644
--- a/checkpolicy/module_compiler.c
+++ b/checkpolicy/module_compiler.c
@@ -1278,18 +1278,6 @@ void append_role_allow(role_allow_rule_t * role_allow_rules)
 	decl->role_allow_rules = role_allow_rules;
 }
 
-/* this doesn't actually append, but really prepends it */
-void append_filename_trans(filename_trans_rule_t * filename_trans_rules)
-{
-	avrule_decl_t *decl = stack_top->decl;
-
-	/* filename transitions are not allowed within conditionals */
-	assert(stack_top->type == 1);
-
-	filename_trans_rules->next = decl->filename_trans_rules;
-	decl->filename_trans_rules = filename_trans_rules;
-}
-
 /* this doesn't actually append, but really prepends it */
 void append_range_trans(range_trans_rule_t * range_tr_rules)
 {
diff --git a/checkpolicy/module_compiler.h b/checkpolicy/module_compiler.h
index 29b824b4..6f8bb9b9 100644
--- a/checkpolicy/module_compiler.h
+++ b/checkpolicy/module_compiler.h
@@ -83,7 +83,6 @@ void append_avrule(avrule_t * avrule);
 void append_role_trans(role_trans_rule_t * role_tr_rules);
 void append_role_allow(role_allow_rule_t * role_allow_rules);
 void append_range_trans(range_trans_rule_t * range_tr_rules);
-void append_filename_trans(filename_trans_rule_t * filename_trans_rules);
 
 /* Create a new optional block and add it to the global policy.
  * During the second pass resolve the block's requirements.  Return 0
diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index dc2ee8f3..25dbf25d 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -1601,7 +1601,7 @@ static int set_types(type_set_t * set, char *id, int *add, char starallowed)
 	return -1;
 }
 
-static int define_compute_type_helper(int which, avrule_t ** rule)
+static int define_compute_type_helper(int which, avrule_t ** rule, int has_filename)
 {
 	char *id;
 	type_datum_t *datum;
@@ -1669,6 +1669,14 @@ static int define_compute_type_helper(int which, avrule_t ** rule)
 	}
 	free(id);
 
+	if (has_filename) {
+		avrule->object_name = queue_remove(id_queue);
+		if (!avrule->object_name) {
+			yyerror("no object_name?");
+			goto bad;
+		}
+	}
+
 	ebitmap_for_each_positive_bit(&tclasses, node, i) {
 		perm = malloc(sizeof(class_perm_node_t));
 		if (!perm) {
@@ -1692,7 +1700,7 @@ static int define_compute_type_helper(int which, avrule_t ** rule)
 	return -1;
 }
 
-int define_compute_type(int which)
+int define_compute_type(int which, int has_filename)
 {
 	char *id;
 	avrule_t *avrule;
@@ -1706,10 +1714,14 @@ int define_compute_type(int which)
 			free(id);
 		id = queue_remove(id_queue);
 		free(id);
+		if (has_filename) {
+			id = queue_remove(id_queue);
+			free(id);
+		}
 		return 0;
 	}
 
-	if (define_compute_type_helper(which, &avrule))
+	if (define_compute_type_helper(which, &avrule, has_filename))
 		return -1;
 
 	append_avrule(avrule);
@@ -1733,7 +1745,7 @@ avrule_t *define_cond_compute_type(int which)
 		return (avrule_t *) 1;
 	}
 
-	if (define_compute_type_helper(which, &avrule))
+	if (define_compute_type_helper(which, &avrule, 0))
 		return COND_ERR;
 
 	return avrule;
@@ -2375,6 +2387,13 @@ static int avrule_cpy(avrule_t *dest, const avrule_t *src)
 		yyerror("out of memory");
 		return -1;
 	}
+	if (src->object_name) {
+		dest->object_name = strdup(src->object_name);
+		if (!dest->object_name) {
+			yyerror("out of memory");
+			return -1;
+		}
+	}
 	dest->line = src->line;
 	dest->source_filename = strdup(source_file);
 	if (!dest->source_filename) {
@@ -3343,194 +3362,6 @@ avrule_t *define_cond_filename_trans(void)
 	return COND_ERR;
 }
 
-int define_filename_trans(void)
-{
-	char *id, *name = NULL;
-	type_set_t stypes, ttypes;
-	ebitmap_t e_stypes, e_ttypes;
-	ebitmap_t e_tclasses;
-	ebitmap_node_t *snode, *tnode, *cnode;
-	filename_trans_rule_t *ftr;
-	type_datum_t *typdatum;
-	avtab_key_t avt_key;
-	uint32_t otype;
-	unsigned int c, s, t;
-	int add, self, rc;
-
-	if (pass == 1) {
-		/* stype */
-		while ((id = queue_remove(id_queue)))
-			free(id);
-		/* ttype */
-		while ((id = queue_remove(id_queue)))
-			free(id);
-		/* tclass */
-		while ((id = queue_remove(id_queue)))
-			free(id);
-		/* otype */
-		id = queue_remove(id_queue);
-		free(id);
-		/* name */
-		id = queue_remove(id_queue);
-		free(id);
-		return 0;
-	}
-
-	type_set_init(&stypes);
-	type_set_init(&ttypes);
-	ebitmap_init(&e_stypes);
-	ebitmap_init(&e_ttypes);
-	ebitmap_init(&e_tclasses);
-
-	add = 1;
-	while ((id = queue_remove(id_queue))) {
-		if (set_types(&stypes, id, &add, 0))
-			goto bad;
-	}
-
-	self = 0;
-	add = 1;
-	while ((id = queue_remove(id_queue))) {
-		if (strcmp(id, "self") == 0) {
-			free(id);
-			if (add == 0) {
-				yyerror("-self is not supported");
-				goto bad;
-			}
-			self = 1;
-			continue;
-		}
-		if (set_types(&ttypes, id, &add, 0))
-			goto bad;
-	}
-
-	if (read_classes(&e_tclasses))
-		goto bad;
-
-	id = (char *)queue_remove(id_queue);
-	if (!id) {
-		yyerror("no otype in transition definition?");
-		goto bad;
-	}
-	if (!is_id_in_scope(SYM_TYPES, id)) {
-		yyerror2("type %s is not within scope", id);
-		free(id);
-		goto bad;
-	}
-	typdatum = hashtab_search(policydbp->p_types.table, id);
-	if (!typdatum) {
-		yyerror2("unknown type %s used in transition definition", id);
-		free(id);
-		goto bad;
-	}
-	free(id);
-	otype = typdatum->s.value;
-
-	name = queue_remove(id_queue);
-	if (!name) {
-		yyerror("no pathname specified in filename_trans definition?");
-		goto bad;
-	}
-
-	/* We expand the class set into separate rules.  We expand the types
-	 * just to make sure there are not duplicates.  They will get turned
-	 * into separate rules later */
-	if (type_set_expand(&stypes, &e_stypes, policydbp, 1))
-		goto bad;
-
-	if (type_set_expand(&ttypes, &e_ttypes, policydbp, 1))
-		goto bad;
-
-	ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) {
-		ebitmap_for_each_positive_bit(&e_stypes, snode, s) {
-			ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) {
-				avt_key.specified = AVTAB_TRANSITION;
-				avt_key.source_type = s + 1;
-				avt_key.target_type = t + 1;
-				avt_key.target_class = c + 1;
-				rc = avtab_insert_filename_trans(
-					&policydbp->te_avtab, &avt_key, otype,
-					name, NULL
-				);
-				if (rc != SEPOL_OK) {
-					if (rc == SEPOL_EEXIST) {
-						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;
-					}
-					yyerror("out of memory");
-					goto bad;
-				}
-			}
-			if (self) {
-				avt_key.specified = AVTAB_TRANSITION;
-				avt_key.source_type = s + 1;
-				avt_key.target_type = t + 1;
-				avt_key.target_class = c + 1;
-				rc = avtab_insert_filename_trans(
-					&policydbp->te_avtab, &avt_key, otype,
-					name, NULL
-				);
-				if (rc != SEPOL_OK) {
-					if (rc == SEPOL_EEXIST) {
-						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[s],
-							policydbp->p_class_val_to_name[c]);
-						goto bad;
-					}
-					yyerror("out of memory");
-					goto bad;
-				}
-			}
-		}
-	
-		/* Now add the real rule since we didn't find any duplicates */
-		ftr = malloc(sizeof(*ftr));
-		if (!ftr) {
-			yyerror("out of memory");
-			goto bad;
-		}
-		filename_trans_rule_init(ftr);
-		append_filename_trans(ftr);
-
-		ftr->name = strdup(name);
-		if (type_set_cpy(&ftr->stypes, &stypes)) {
-			yyerror("out of memory");
-			goto bad;
-		}
-		if (type_set_cpy(&ftr->ttypes, &ttypes)) {
-			yyerror("out of memory");
-			goto bad;
-		}
-		ftr->tclass = c + 1;
-		ftr->otype = otype;
-		ftr->flags = self ? RULE_SELF : 0;
-	}
-
-	free(name);
-	ebitmap_destroy(&e_stypes);
-	ebitmap_destroy(&e_ttypes);
-	ebitmap_destroy(&e_tclasses);
-	type_set_destroy(&stypes);
-	type_set_destroy(&ttypes);
-
-	return 0;
-
-bad:
-	free(name);
-	ebitmap_destroy(&e_stypes);
-	ebitmap_destroy(&e_ttypes);
-	ebitmap_destroy(&e_tclasses);
-	type_set_destroy(&stypes);
-	type_set_destroy(&ttypes);
-	return -1;
-}
-
 static constraint_expr_t *constraint_expr_clone(const constraint_expr_t * expr)
 {
 	constraint_expr_t *h = NULL, *l = NULL, *newe;
diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
index 50a7ba78..5d0f70e4 100644
--- a/checkpolicy/policy_define.h
+++ b/checkpolicy/policy_define.h
@@ -28,7 +28,7 @@ int define_default_role(int which);
 int define_default_type(int which);
 int define_default_range(int which);
 int define_common_perms(void);
-int define_compute_type(int which);
+int define_compute_type(int which, int has_filename);
 int define_conditional(cond_expr_t *expr, avrule_t *t_list, avrule_t *f_list );
 int define_constraint(constraint_expr_t *expr);
 int define_dominance(void);
@@ -57,7 +57,6 @@ int define_role_trans(int class_specified);
 int define_role_types(void);
 int define_role_attr(void);
 int define_roleattribute(void);
-int define_filename_trans(void);
 int define_sens(void);
 int define_te_avtab(int which);
 int define_te_avtab_extended_perms(int which);
diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
index da32a776..2a14fc1e 100644
--- a/checkpolicy/policy_parse.y
+++ b/checkpolicy/policy_parse.y
@@ -451,13 +451,13 @@ cond_dontaudit_def	: DONTAUDIT names names ':' names names ';'
 		        ;
 			;
 transition_def		: TYPE_TRANSITION  names names ':' names identifier filename ';'
-			{if (define_filename_trans()) return -1; }
+			{if (define_compute_type(AVRULE_TRANSITION, 1)) return -1; }
 			| TYPE_TRANSITION names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_TRANSITION)) return -1;}
+                        {if (define_compute_type(AVRULE_TRANSITION, 0)) return -1;}
                         | TYPE_MEMBER names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_MEMBER)) return -1;}
+                        {if (define_compute_type(AVRULE_MEMBER, 0)) return -1;}
                         | TYPE_CHANGE names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_CHANGE)) return -1;}
+                        {if (define_compute_type(AVRULE_CHANGE, 0)) return -1;}
     			;
 range_trans_def		: RANGE_TRANSITION names names mls_range_def ';'
 			{ if (define_range_trans(0)) return -1; }
diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
index 515fc9a5..cde80f95 100644
--- a/checkpolicy/test/dismod.c
+++ b/checkpolicy/test/dismod.c
@@ -50,7 +50,6 @@
 #define DISPLAY_AVBLOCK_ROLE_ALLOW	4
 #define DISPLAY_AVBLOCK_REQUIRES	5
 #define DISPLAY_AVBLOCK_DECLARES	6
-#define DISPLAY_AVBLOCK_FILENAME_TRANS	7
 
 static policydb_t policydb;
 extern unsigned int ss_initialized;
@@ -90,7 +89,6 @@ static struct command {
 	{CMD,       'c', "Display policy capabilities"},
 	{CMD|NOOPT, 'l', "Link in a module"},
 	{CMD,       'u', "Display the unknown handling setting"},
-	{CMD,       'F', "Display filename_trans rules"},
 	{HEADER, 0, ""},
 	{CMD|NOOPT, 'f',  "set output file"},
 	{CMD|NOOPT, 'm',  "display menu"},
@@ -339,6 +337,8 @@ static int display_avrule(avrule_t * avrule, policydb_t * policy,
 				   policy, fp);
 	} else if (avrule->specified & AVRULE_TYPE) {
 		display_id(policy, fp, SYM_TYPES, avrule->perms->data - 1, "");
+		if (avrule->object_name)
+			fprintf(fp, " \"%s\"", avrule->object_name);
 	} else if (avrule->specified & AVRULE_XPERMS) {
 		avtab_extended_perms_t xperms;
 		int i;
@@ -556,18 +556,6 @@ static void display_role_allow(role_allow_rule_t * ra, policydb_t * p, FILE * fp
 	}
 }
 
-static void display_filename_trans(filename_trans_rule_t * tr, policydb_t * p, FILE * fp)
-{
-	fprintf(fp, "filename transition");
-	for (; tr; tr = tr->next) {
-		display_type_set(&tr->stypes, 0, p, fp);
-		display_type_set(&tr->ttypes, 0, p, fp);
-		display_id(p, fp, SYM_CLASSES, tr->tclass - 1, ":");
-		display_id(p, fp, SYM_TYPES, tr->otype - 1, "");
-		fprintf(fp, " %s\n", tr->name);
-	}
-}
-
 static int role_display_callback(hashtab_key_t key __attribute__((unused)),
 			  hashtab_datum_t datum, void *data)
 {
@@ -732,10 +720,6 @@ static int display_avdecl(avrule_decl_t * decl, int field,
 			}
 			break;
 		}
-	case DISPLAY_AVBLOCK_FILENAME_TRANS:
-		display_filename_trans(decl->filename_trans_rules, policy,
-				       out_fp);
-		break;
 	default:{
 			assert(0);
 		}
@@ -1061,11 +1045,6 @@ int main(int argc, char **argv)
 			if (out_fp != stdout)
 				printf("\nOutput to file: %s\n", OutfileName);
 			break;
-		case 'F':
-			fprintf(out_fp, "filename_trans rules:\n");
-			display_avblock(DISPLAY_AVBLOCK_FILENAME_TRANS,
-					&policydb, out_fp);
-			break;
 		case 'l':
 			link_module(&policydb, out_fp);
 			break;
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index 7150d405..996bad70 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -4650,6 +4650,7 @@ static avrule_t *__cil_init_sepol_avrule(uint32_t kind, struct cil_tree_node *no
 	__cil_init_sepol_type_set(&avrule->stypes);
 	__cil_init_sepol_type_set(&avrule->ttypes);
 	avrule->perms = NULL;
+	avrule->object_name = NULL;
 	avrule->line = node->line;
 
 	avrule->source_filename = NULL;
@@ -4676,6 +4677,7 @@ static void __cil_destroy_sepol_avrules(avrule_t *curr)
 		ebitmap_destroy(&curr->stypes.negset);
 		ebitmap_destroy(&curr->ttypes.types);
 		ebitmap_destroy(&curr->ttypes.negset);
+		free(curr->object_name);
 		__cil_destroy_sepol_class_perms(curr->perms);
 		free(curr);
 		curr = next;
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index 8bb11d18..d30f26af 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -278,6 +278,7 @@ typedef struct avrule {
 	type_set_t stypes;
 	type_set_t ttypes;
 	class_perm_node_t *perms;
+	char *object_name;	/* optional object name */
 	av_extended_perms_t *xperms;
 	unsigned long line;	/* line number from policy.conf where
 				 * this rule originated  */
@@ -301,16 +302,6 @@ typedef struct role_allow_rule {
 	struct role_allow_rule *next;
 } role_allow_rule_t;
 
-typedef struct filename_trans_rule {
-	uint32_t flags; /* may have RULE_SELF set */
-	type_set_t stypes;
-	type_set_t ttypes;
-	uint32_t tclass;
-	char *name;
-	uint32_t otype;	/* new type */
-	struct filename_trans_rule *next;
-} filename_trans_rule_t;
-
 typedef struct range_trans_rule {
 	type_set_t stypes;
 	type_set_t ttypes;
@@ -451,9 +442,6 @@ typedef struct avrule_decl {
 	scope_index_t required;	/* symbols needed to activate this block */
 	scope_index_t declared;	/* symbols declared within this block */
 
-	/* type transition rules with a 'name' component */
-	filename_trans_rule_t *filename_trans_rules;
-
 	/* for additive statements (type attribute, roles, and users) */
 	symtab_t symtab[SYM_NUM];
 
@@ -656,8 +644,6 @@ extern void avrule_destroy(avrule_t * x);
 extern void avrule_list_destroy(avrule_t * x);
 extern void role_trans_rule_init(role_trans_rule_t * x);
 extern void role_trans_rule_list_destroy(role_trans_rule_t * x);
-extern void filename_trans_rule_init(filename_trans_rule_t * x);
-extern void filename_trans_rule_list_destroy(filename_trans_rule_t * x);
 
 extern void role_datum_init(role_datum_t * x);
 extern void role_datum_destroy(role_datum_t * x);
diff --git a/libsepol/src/avrule_block.c b/libsepol/src/avrule_block.c
index dcfce8b8..fce4e772 100644
--- a/libsepol/src/avrule_block.c
+++ b/libsepol/src/avrule_block.c
@@ -99,7 +99,6 @@ void avrule_decl_destroy(avrule_decl_t * x)
 	cond_list_destroy(x->cond_list);
 	avrule_list_destroy(x->avrules);
 	role_trans_rule_list_destroy(x->role_tr_rules);
-	filename_trans_rule_list_destroy(x->filename_trans_rules);
 	role_allow_rule_list_destroy(x->role_allow_rules);
 	range_trans_rule_list_destroy(x->range_tr_rules);
 	scope_index_destroy(&x->required);
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index 878b0f21..a4c92f4f 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -1407,94 +1407,6 @@ static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules)
 	return 0;
 }
 
-static int expand_filename_trans_helper(expand_state_t *state,
-					filename_trans_rule_t *rule,
-					unsigned int s, unsigned int t)
-{
-	uint32_t mapped_otype, present_otype;
-	int rc;
-	avtab_key_t avt_key;
-
-	mapped_otype = state->typemap[rule->otype - 1];
-
-	avt_key.specified = AVTAB_TRANSITION;
-	avt_key.source_type = s + 1;
-	avt_key.target_type = t + 1;
-	avt_key.target_class = rule->tclass;
-
-	rc = avtab_insert_filename_trans(&state->out->te_avtab, &avt_key,
-		mapped_otype, rule->name, &present_otype);
-	if (rc == SEPOL_EEXIST) {
-		/* duplicate rule, ignore */
-		if (present_otype == mapped_otype)
-			return 0;
-
-		ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
-		    state->out->p_type_val_to_name[s],
-		    state->out->p_type_val_to_name[t],
-		    state->out->p_class_val_to_name[rule->tclass - 1],
-		    rule->name,
-		    state->out->p_type_val_to_name[present_otype - 1],
-		    state->out->p_type_val_to_name[mapped_otype - 1]);
-		return -1;
-	} else if (rc < 0) {
-		ERR(state->handle, "Out of memory!");
-		return -1;
-	}
-	return 0;
-}
-
-static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
-{
-	unsigned int i, j;
-	filename_trans_rule_t *cur_rule;
-	ebitmap_t stypes, ttypes;
-	ebitmap_node_t *snode, *tnode;
-	int rc;
-
-	cur_rule = rules;
-	while (cur_rule) {
-		ebitmap_init(&stypes);
-		ebitmap_init(&ttypes);
-
-		if (expand_convert_type_set(state->out, state->typemap,
-					    &cur_rule->stypes, &stypes, 1)) {
-			ERR(state->handle, "Out of memory!");
-			return -1;
-		}
-
-		if (expand_convert_type_set(state->out, state->typemap,
-					    &cur_rule->ttypes, &ttypes, 1)) {
-			ERR(state->handle, "Out of memory!");
-			return -1;
-		}
-
-
-		ebitmap_for_each_positive_bit(&stypes, snode, i) {
-			ebitmap_for_each_positive_bit(&ttypes, tnode, j) {
-				rc = expand_filename_trans_helper(
-					state, cur_rule, i, j
-				);
-				if (rc)
-					return rc;
-			}
-			if (cur_rule->flags & RULE_SELF) {
-				rc = expand_filename_trans_helper(
-					state, cur_rule, i, i
-				);
-				if (rc)
-					return rc;
-			}
-		}
-
-		ebitmap_destroy(&stypes);
-		ebitmap_destroy(&ttypes);
-
-		cur_rule = cur_rule->next;
-	}
-	return 0;
-}
-
 static int exp_rangetr_helper(uint32_t stype, uint32_t ttype, uint32_t tclass,
 			      mls_semantic_range_t * trange,
 			      expand_state_t * state)
@@ -1708,7 +1620,7 @@ static int expand_terule_helper(sepol_handle_t * handle,
 				uint32_t specified, cond_av_list_t ** cond,
 				cond_av_list_t ** other, uint32_t stype,
 				uint32_t ttype, class_perm_node_t * perms,
-				avtab_t * avtab, int enabled)
+				char *object_name, avtab_t * avtab, int enabled)
 {
 	avtab_key_t avkey;
 	avtab_datum_t *avdatump;
@@ -1732,6 +1644,34 @@ static int expand_terule_helper(sepol_handle_t * handle,
 		    typemap ? typemap[cur->data - 1] : cur->data;
 		avkey.target_class = cur->tclass;
 
+		/*
+		 * if expanded node is a filename transition, insert it, insert
+		 * function checks for duplicates
+		 */
+		if (specified & AVRULE_TRANSITION && object_name) {
+			int rc = avtab_insert_filename_trans(avtab, &avkey,
+							     remapped_data,
+							     object_name,
+							     &oldtype);
+			if (rc == SEPOL_EEXIST) {
+				ERR(handle, "conflicting filename transition %s %s:%s \"%s\": %s vs %s",
+				    p->p_type_val_to_name[avkey.source_type - 1],
+				    p->p_type_val_to_name[avkey.target_type - 1],
+				    p->p_class_val_to_name[avkey.target_class - 1],
+				    object_name,
+				    p->p_type_val_to_name[oldtype - 1],
+				    p->p_type_val_to_name[remapped_data - 1]);
+				return EXPAND_RULE_CONFLICT;
+			}
+			if (rc < 0)
+				return EXPAND_RULE_ERROR;
+			/*
+			 * filename transtion inserted, continue with next node
+			 */
+			cur = cur->next;
+			continue;
+		}
+
 		conflict = 0;
 		/* check to see if the expanded TE already exists --
 		 * either in the global scope or in another
@@ -1777,12 +1717,9 @@ static int expand_terule_helper(sepol_handle_t * handle,
 				    || node->parse_context == cond)
 					return EXPAND_RULE_SUCCESS;
 				ERR(handle, "duplicate TE rule for %s %s:%s %s",
-				    p->p_type_val_to_name[avkey.source_type -
-							  1],
-				    p->p_type_val_to_name[avkey.target_type -
-							  1],
-				    p->p_class_val_to_name[avkey.target_class -
-							   1],
+				    p->p_type_val_to_name[avkey.source_type - 1],
+				    p->p_type_val_to_name[avkey.target_type - 1],
+				    p->p_class_val_to_name[avkey.target_class - 1],
 				    p->p_type_val_to_name[oldtype - 1]);
 				return EXPAND_RULE_CONFLICT;
 			}
@@ -1947,6 +1884,7 @@ static int expand_rule_helper(sepol_handle_t * handle,
 				retval = expand_terule_helper(handle, p, typemap,
 							      source_rule->specified, cond,
 							      other, i, i, source_rule->perms,
+							      source_rule->object_name,
 							      dest_avtab, enabled);
 				if (retval != EXPAND_RULE_SUCCESS)
 					return retval;
@@ -1963,6 +1901,7 @@ static int expand_rule_helper(sepol_handle_t * handle,
 				retval = expand_terule_helper(handle, p, typemap,
 							      source_rule->specified, cond,
 							      other, i, j, source_rule->perms,
+							      source_rule->object_name,
 							      dest_avtab, enabled);
 				if (retval != EXPAND_RULE_SUCCESS)
 					return retval;
@@ -2791,9 +2730,6 @@ static int copy_and_expand_avrule_block(expand_state_t * state)
 			goto cleanup;
 		}
 
-		if (expand_filename_trans(state, decl->filename_trans_rules))
-			goto cleanup;
-
 		/* expand the range transition rules */
 		if (expand_range_trans(state, decl->range_tr_rules))
 			goto cleanup;
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index 3b7742bc..88b23594 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -1249,6 +1249,12 @@ static int copy_avrule_list(avrule_t * list, avrule_t ** dst,
 			goto cleanup;
 		}
 
+		if (cur->object_name) {
+			new_rule->object_name = strdup(cur->object_name);
+			if (!new_rule->object_name)
+				goto cleanup;
+		}
+
 		cur_perm = cur->perms;
 		tail_perm = NULL;
 		while (cur_perm) {
@@ -1412,51 +1418,6 @@ static int copy_role_allow_list(role_allow_rule_t * list,
 	return -1;
 }
 
-static int copy_filename_trans_list(filename_trans_rule_t * list,
-				    filename_trans_rule_t ** dst,
-				    policy_module_t * module,
-				    link_state_t * state)
-{
-	filename_trans_rule_t *cur, *new_rule, *tail;
-
-	cur = list;
-	tail = *dst;
-	while (tail && tail->next)
-		tail = tail->next;
-
-	while (cur) {
-		new_rule = malloc(sizeof(*new_rule));
-		if (!new_rule)
-			goto err;
-
-		filename_trans_rule_init(new_rule);
-
-		if (*dst == NULL)
-			*dst = new_rule;
-		else
-			tail->next = new_rule;
-		tail = new_rule;
-
-		new_rule->name = strdup(cur->name);
-		if (!new_rule->name)
-			goto err;
-
-		if (type_set_or_convert(&cur->stypes, &new_rule->stypes, module) ||
-		    type_set_or_convert(&cur->ttypes, &new_rule->ttypes, module))
-			goto err;
-
-		new_rule->tclass = module->map[SYM_CLASSES][cur->tclass - 1];
-		new_rule->otype = module->map[SYM_TYPES][cur->otype - 1];
-		new_rule->flags = cur->flags;
-
-		cur = cur->next;
-	}
-	return 0;
-err:
-	ERR(state->handle, "Out of memory!");
-	return -1;
-}
-
 static int copy_range_trans_list(range_trans_rule_t * rules,
 				 range_trans_rule_t ** dst,
 				 policy_module_t * mod, link_state_t * state)
@@ -1679,11 +1640,6 @@ static int copy_avrule_decl(link_state_t * state, policy_module_t * module,
 		return -1;
 	}
 
-	if (copy_filename_trans_list(src_decl->filename_trans_rules,
-				     &dest_decl->filename_trans_rules,
-				     module, state))
-		return -1;
-
 	if (copy_range_trans_list(src_decl->range_tr_rules,
 				  &dest_decl->range_tr_rules, module, state))
 		return -1;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 1a490089..1e002c4e 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -547,7 +547,7 @@ static int semantic_level_to_cil(struct policydb *pdb, int sens_offset, struct m
 	return 0;
 }
 
-static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const struct class_perm_node *classperms)
+static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const char *object_name, const struct class_perm_node *classperms)
 {
 	int rc = -1;
 	const char *rule;
@@ -597,6 +597,12 @@ static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const
 					rule, src, tgt,
 					pdb->p_class_val_to_name[classperm->tclass - 1],
 					perms + 1);
+		} else if (object_name) {
+			cil_println(indent, "(%s %s %s %s \"%s\" %s)",
+					rule, src, tgt,
+					pdb->p_class_val_to_name[classperm->tclass - 1],
+					object_name,
+					pdb->p_type_val_to_name[classperm->data - 1]);
 		} else {
 			cil_println(indent, "(%s %s %s %s %s)",
 					rule, src, tgt,
@@ -1199,7 +1205,7 @@ static int avrule_list_to_cil(int indent, struct policydb *pdb, struct avrule *a
 				if (avrule->specified & AVRULE_XPERMS) {
 					rc = avrulex_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->perms, avrule->xperms);
 				} else {
-					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->perms);
+					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->object_name, avrule->perms);
 				}
 				if (rc != 0) {
 					goto exit;
@@ -1210,7 +1216,7 @@ static int avrule_list_to_cil(int indent, struct policydb *pdb, struct avrule *a
 				if (avrule->specified & AVRULE_XPERMS) {
 					rc = avrulex_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->perms, avrule->xperms);
 				} else {
-					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->perms);
+					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->object_name, avrule->perms);
 				}
 				if (rc != 0) {
 					goto exit;
@@ -1576,60 +1582,6 @@ exit:
 	return rc;
 }
 
-static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filename_trans_rule *rules, struct list *attr_list)
-{
-	int rc = -1;
-	char **stypes = NULL;
-	unsigned int num_stypes = 0;
-	unsigned int stype;
-	char **ttypes = NULL;
-	unsigned int num_ttypes = 0;
-	unsigned int ttype;
-	struct type_set *ts;
-	struct filename_trans_rule *rule;
-
-	for (rule = rules; rule != NULL; rule = rule->next) {
-		ts = &rule->stypes;
-		rc = process_typeset(pdb, ts, attr_list, &stypes, &num_stypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		ts = &rule->ttypes;
-		rc = process_typeset(pdb, ts, attr_list, &ttypes, &num_ttypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (stype = 0; stype < num_stypes; stype++) {
-			for (ttype = 0; ttype < num_ttypes; ttype++) {
-				cil_println(indent, "(typetransition %s %s %s \"%s\" %s)",
-					    stypes[stype], ttypes[ttype],
-					    pdb->p_class_val_to_name[rule->tclass - 1],
-					    rule->name,
-					    pdb->p_type_val_to_name[rule->otype - 1]);
-			}
-			if (rule->flags & RULE_SELF) {
-				cil_println(indent, "(typetransition %s self %s \"%s\" %s)",
-					    stypes[stype],
-					    pdb->p_class_val_to_name[rule->tclass - 1],
-					    rule->name,
-					    pdb->p_type_val_to_name[rule->otype - 1]);
-			}
-		}
-
-		names_destroy(&stypes, &num_stypes);
-		names_destroy(&ttypes, &num_ttypes);
-	}
-
-	rc = 0;
-exit:
-	names_destroy(&stypes, &num_stypes);
-	names_destroy(&ttypes, &num_ttypes);
-
-	return rc;
-}
-
 struct class_perm_datum {
 	char *name;
 	uint32_t val;
@@ -3683,11 +3635,6 @@ static int block_to_cil(struct policydb *pdb, struct avrule_block *block, struct
 		goto exit;
 	}
 
-	rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules, type_attr_list);
-	if (rc != 0) {
-		goto exit;
-	}
-
 	rc = cond_list_to_cil(indent, pdb, decl->cond_list, type_attr_list);
 	if (rc != 0) {
 		goto exit;
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index c1ce9c34..4913ee21 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -638,6 +638,7 @@ void avrule_destroy(avrule_t * x)
 	}
 
 	free(x->xperms);
+	free(x->object_name);
 }
 
 void role_trans_rule_init(role_trans_rule_t * x)
@@ -667,33 +668,6 @@ void role_trans_rule_list_destroy(role_trans_rule_t * x)
 	}
 }
 
-void filename_trans_rule_init(filename_trans_rule_t * x)
-{
-	memset(x, 0, sizeof(*x));
-	type_set_init(&x->stypes);
-	type_set_init(&x->ttypes);
-}
-
-static void filename_trans_rule_destroy(filename_trans_rule_t * x)
-{
-	if (!x)
-		return;
-	type_set_destroy(&x->stypes);
-	type_set_destroy(&x->ttypes);
-	free(x->name);
-}
-
-void filename_trans_rule_list_destroy(filename_trans_rule_t * x)
-{
-	filename_trans_rule_t *next;
-	while (x) {
-		next = x->next;
-		filename_trans_rule_destroy(x);
-		free(x);
-		x = next;
-	}
-}
-
 void role_allow_rule_init(role_allow_rule_t * x)
 {
 	memset(x, 0, sizeof(role_allow_rule_t));
@@ -3493,31 +3467,32 @@ static int role_allow_rule_read(role_allow_rule_t ** r, struct policy_file *fp)
 	return 0;
 }
 
-static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r,
+static int filename_trans_rule_read(policydb_t *p, avrule_t **r,
 				    struct policy_file *fp)
 {
 	uint32_t buf[3], nel, i, len;
 	unsigned int entries;
-	filename_trans_rule_t *ftr, *lftr;
+	avrule_t *cur;
 	int rc;
 
 	rc = next_entry(buf, fp, sizeof(uint32_t));
 	if (rc < 0)
 		return -1;
 	nel = le32_to_cpu(buf[0]);
-	lftr = NULL;
 	for (i = 0; i < nel; i++) {
-		ftr = malloc(sizeof(*ftr));
-		if (!ftr)
+		cur = malloc(sizeof(avrule_t));
+		if (!cur)
 			return -1;
+		avrule_init(cur);
 
-		filename_trans_rule_init(ftr);
+		cur->next = *r;
+		*r = cur;
 
-		if (lftr)
-			lftr->next = ftr;
-		else
-			*r = ftr;
-		lftr = ftr;
+		cur->specified = AVRULE_TRANSITION;
+		cur->perms = malloc(sizeof(class_perm_node_t));
+		if (!cur->perms)
+			return -1;
+		class_perm_node_init(cur->perms);
 
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0)
@@ -3527,19 +3502,14 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r,
 		if (zero_or_saturated(len))
 			return -1;
 
-		ftr->name = malloc(len + 1);
-		if (!ftr->name)
-			return -1;
-
-		rc = next_entry(ftr->name, fp, len);
+		rc = str_read(&cur->object_name, fp, len);
 		if (rc)
 			return -1;
-		ftr->name[len] = 0;
 
-		if (type_set_read(&ftr->stypes, fp))
+		if (type_set_read(&cur->stypes, fp))
 			return -1;
 
-		if (type_set_read(&ftr->ttypes, fp))
+		if (type_set_read(&cur->ttypes, fp))
 			return -1;
 
 		if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS)
@@ -3550,10 +3520,10 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r,
 		rc = next_entry(buf, fp, sizeof(uint32_t) * entries);
 		if (rc < 0)
 			return -1;
-		ftr->tclass = le32_to_cpu(buf[0]);
-		ftr->otype = le32_to_cpu(buf[1]);
+		cur->perms->tclass = le32_to_cpu(buf[0]);
+		cur->perms->data = le32_to_cpu(buf[1]);
 		if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS)
-			ftr->flags = le32_to_cpu(buf[2]);
+			cur->flags = le32_to_cpu(buf[2]);
 	}
 
 	return 0;
@@ -3656,7 +3626,7 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
 	}
 
 	if (p->policyvers >= MOD_POLICYDB_VERSION_FILENAME_TRANS &&
-	    filename_trans_rule_read(p, &decl->filename_trans_rules, fp))
+	    filename_trans_rule_read(p, &decl->avrules, fp))
 		return -1;
 
 	if (p->policyvers >= MOD_POLICYDB_VERSION_RANGETRANS &&
diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 89306185..0b8e8eee 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -1313,31 +1313,6 @@ bad:
 	return -1;
 }
 
-
-static int validate_filename_trans_rules(sepol_handle_t *handle, const filename_trans_rule_t *filename_trans, const policydb_t *p, validate_t flavors[])
-{
-	for (; filename_trans; filename_trans = filename_trans->next) {
-		if (validate_type_set(&filename_trans->stypes, &flavors[SYM_TYPES]))
-			goto bad;
-		if (validate_type_set(&filename_trans->ttypes, &flavors[SYM_TYPES]))
-			goto bad;
-		if (validate_value(filename_trans->tclass,&flavors[SYM_CLASSES] ))
-			goto bad;
-		if (validate_simpletype(filename_trans->otype, p, flavors))
-			goto bad;
-
-		/* currently only the RULE_SELF flag can be set */
-		if ((filename_trans->flags & ~RULE_SELF) != 0)
-			goto bad;
-	}
-
-	return 0;
-
-bad:
-	ERR(handle, "Invalid filename trans rule list");
-	return -1;
-}
-
 static int validate_symtabs(sepol_handle_t *handle, const symtab_t symtabs[], validate_t flavors[])
 {
 	unsigned int i;
@@ -1372,8 +1347,6 @@ static int validate_avrule_blocks(sepol_handle_t *handle, const avrule_block_t *
 				goto bad;
 			if (validate_scope_index(handle, &decl->declared, flavors))
 				goto bad;
-			if (validate_filename_trans_rules(handle, decl->filename_trans_rules, p, flavors))
-				goto bad;
 			if (validate_symtabs(handle, decl->symtab, flavors))
 				goto bad;
 		}
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index c4d593ab..2035b350 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -1970,6 +1970,10 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
 	uint32_t buf[32], len;
 	class_perm_node_t *cur;
 
+	/* skip filename transitions for now */
+	if (avrule->specified & AVRULE_TRANSITION && avrule->object_name)
+		return POLICYDB_SUCCESS;
+
 	if (p->policyvers < MOD_POLICYDB_VERSION_SELF_TYPETRANS &&
 	    (avrule->specified & AVRULE_TYPE) &&
 	    (avrule->flags & RULE_SELF)) {
@@ -2063,7 +2067,9 @@ static int avrule_write_list(policydb_t *p, avrule_t * avrules,
 	avrule = avrules;
 	len = 0;
 	while (avrule) {
-		len++;
+		if (!(avrule->specified & AVRULE_TRANSITION &&
+		      avrule->object_name))
+			len++;
 		avrule = avrule->next;
 	}
 
@@ -2162,55 +2168,67 @@ static int role_allow_rule_write(role_allow_rule_t * r, struct policy_file *fp)
 	return POLICYDB_SUCCESS;
 }
 
-static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t,
+static int filename_trans_rule_write(policydb_t *p, avrule_t *rules,
 				     struct policy_file *fp)
 {
 	int nel = 0;
 	size_t items, entries;
 	uint32_t buf[3], len;
-	filename_trans_rule_t *ftr;
+	avrule_t *rule;
+	class_perm_node_t *perm;
 
-	for (ftr = t; ftr; ftr = ftr->next)
-		nel++;
+	for (rule = rules; rule; rule = rule->next) {
+		if (rule->specified & AVRULE_TRANSITION && rule->object_name) {
+			for (perm = rule->perms; perm; perm = perm->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;
+	for (rule = rules; rule; rule = rule->next) {
+		if (!(rule->specified & AVRULE_TRANSITION && rule->object_name))
+			continue;
+		len = strlen(rule->object_name);
+		for (perm = rule->perms; perm; perm = perm->next) {
+			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;
+			items = put_entry(rule->object_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;
+			if (type_set_write(&rule->stypes, fp))
+				return POLICYDB_ERROR;
+			if (type_set_write(&rule->ttypes, fp))
+				return POLICYDB_ERROR;
 
-		buf[0] = cpu_to_le32(ftr->tclass);
-		buf[1] = cpu_to_le32(ftr->otype);
-		buf[2] = cpu_to_le32(ftr->flags);
+			buf[0] = cpu_to_le32(perm->tclass);
+			buf[1] = cpu_to_le32(perm->data);
+			buf[2] = cpu_to_le32(rule->flags);
+
+			if (p->policyvers >=
+			    MOD_POLICYDB_VERSION_SELF_TYPETRANS) {
+				entries = 3;
+			} else if (!(rule->flags & RULE_SELF)) {
+				entries = 2;
+			} else {
+				ERR(fp->handle,
+				    "Module contains a self rule not supported by the target module policy version");
+				return POLICYDB_ERROR;
+			}
 
-		if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) {
-			entries = 3;
-		} else if (!(ftr->flags & RULE_SELF)) {
-			entries = 2;
-		} else {
-			ERR(fp->handle,
-			    "Module contains a self rule not supported by the target module policy version");
-			return POLICYDB_ERROR;
+			items = put_entry(buf, sizeof(uint32_t), entries, fp);
+			if (items != entries)
+				return POLICYDB_ERROR;
 		}
-
-		items = put_entry(buf, sizeof(uint32_t), entries, fp);
-		if (items != entries)
-			return POLICYDB_ERROR;
 	}
 	return POLICYDB_SUCCESS;
 }
@@ -2284,7 +2302,7 @@ static int avrule_decl_write(avrule_decl_t * decl, int num_scope_syms,
 	}
 
 	if (p->policyvers >= MOD_POLICYDB_VERSION_FILENAME_TRANS &&
-	    filename_trans_rule_write(p, decl->filename_trans_rules, fp))
+	    filename_trans_rule_write(p, decl->avrules, fp))
 		return POLICYDB_ERROR;
 
 	if (p->policyvers >= MOD_POLICYDB_VERSION_RANGETRANS &&
