diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index 455255ba..c3c6af72 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -1601,7 +1601,8 @@ 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, int has_filename)
+static int define_compute_type_helper(int which, avrule_t ** rule,
+				      int has_filename, uint8_t name_match)
 {
 	char *id;
 	type_datum_t *datum;
@@ -1676,6 +1677,7 @@ static int define_compute_type_helper(int which, avrule_t ** rule, int has_filen
 			goto bad;
 		}
 	}
+	avrule->name_match = name_match;
 
 	ebitmap_for_each_positive_bit(&tclasses, node, i) {
 		perm = malloc(sizeof(class_perm_node_t));
@@ -1700,7 +1702,7 @@ static int define_compute_type_helper(int which, avrule_t ** rule, int has_filen
 	return -1;
 }
 
-int define_compute_type(int which, int has_filename)
+int define_compute_type(int which, int has_filename, uint8_t name_match)
 {
 	char *id;
 	avrule_t *avrule;
@@ -1721,7 +1723,8 @@ int define_compute_type(int which, int has_filename)
 		return 0;
 	}
 
-	if (define_compute_type_helper(which, &avrule, has_filename))
+	if (define_compute_type_helper(which, &avrule, has_filename,
+				       name_match))
 		return -1;
 
 	append_avrule(avrule);
@@ -1745,7 +1748,8 @@ avrule_t *define_cond_compute_type(int which)
 		return (avrule_t *) 1;
 	}
 
-	if (define_compute_type_helper(which, &avrule, 0))
+	if (define_compute_type_helper(which, &avrule, 0,
+				       NAME_TRANS_MATCH_EXACT))
 		return COND_ERR;
 
 	return avrule;
@@ -2394,6 +2398,7 @@ static int avrule_cpy(avrule_t *dest, const avrule_t *src)
 			return -1;
 		}
 	}
+	dest->name_match = src->name_match;
 	dest->line = src->line;
 	dest->source_filename = strdup(source_file);
 	if (!dest->source_filename) {
diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h
index 5d0f70e4..c1314871 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 has_filename);
+int define_compute_type(int which, int has_filename, uint8_t name_match);
 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);
diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y
index fccbc052..12dff7af 100644
--- a/checkpolicy/policy_parse.y
+++ b/checkpolicy/policy_parse.y
@@ -109,6 +109,7 @@ typedef int (* require_func_t)(int pass);
 %token IF
 %token ELSE
 %token TYPE_TRANSITION
+%token MATCH_EXACT MATCH_PREFIX MATCH_SUFFIX
 %token TYPE_MEMBER
 %token TYPE_CHANGE
 %token ROLE_TRANSITION
@@ -452,13 +453,19 @@ cond_dontaudit_def	: DONTAUDIT names names ':' names names ';'
 		        ;
 			;
 transition_def		: TYPE_TRANSITION  names names ':' names identifier filename ';'
-			{if (define_compute_type(AVRULE_TRANSITION, 1)) return -1; }
+			{if (define_compute_type(AVRULE_TRANSITION, 1, NAME_TRANS_MATCH_EXACT)) return -1;}
+            | TYPE_TRANSITION names names ':' names identifier filename MATCH_EXACT ';'
+            {if (define_compute_type(AVRULE_TRANSITION, 1, NAME_TRANS_MATCH_EXACT)) return -1;}
+            | TYPE_TRANSITION names names ':' names identifier filename MATCH_PREFIX ';'
+            {if (define_compute_type(AVRULE_TRANSITION, 1, NAME_TRANS_MATCH_PREFIX)) return -1;}
+            | TYPE_TRANSITION names names ':' names identifier filename MATCH_SUFFIX ';'
+            {if (define_compute_type(AVRULE_TRANSITION, 1, NAME_TRANS_MATCH_SUFFIX)) return -1;}
 			| TYPE_TRANSITION names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_TRANSITION, 0)) return -1;}
+                        {if (define_compute_type(AVRULE_TRANSITION, 0, NAME_TRANS_MATCH_EXACT)) return -1;}
                         | TYPE_MEMBER names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_MEMBER, 0)) return -1;}
+                        {if (define_compute_type(AVRULE_MEMBER, 0, NAME_TRANS_MATCH_EXACT)) return -1;}
                         | TYPE_CHANGE names names ':' names identifier ';'
-                        {if (define_compute_type(AVRULE_CHANGE, 0)) return -1;}
+                        {if (define_compute_type(AVRULE_CHANGE, 0, NAME_TRANS_MATCH_EXACT)) return -1;}
     			;
 range_trans_def		: RANGE_TRANSITION names names mls_range_def ';'
 			{ if (define_range_trans(0)) return -1; }
diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
index 9fefea7b..3f568701 100644
--- a/checkpolicy/policy_scan.l
+++ b/checkpolicy/policy_scan.l
@@ -125,6 +125,12 @@ EXPANDATTRIBUTE |
 expandattribute                 { return(EXPANDATTRIBUTE); }
 TYPE_TRANSITION |
 type_transition			{ return(TYPE_TRANSITION); }
+MATCH_EXACT |
+match_exact			{ return(MATCH_EXACT); }
+MATCH_PREFIX |
+match_prefix			{ return(MATCH_PREFIX); }
+MATCH_SUFFIX |
+match_suffix			{ return(MATCH_SUFFIX); }
 TYPE_MEMBER |
 type_member			{ return(TYPE_MEMBER); }
 TYPE_CHANGE |
diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
index f26f360c..53edecee 100644
--- a/checkpolicy/test/dismod.c
+++ b/checkpolicy/test/dismod.c
@@ -293,6 +293,20 @@ static int display_avrule(avrule_t * avrule, policydb_t * policy,
 		display_id(policy, fp, SYM_TYPES, avrule->perms->data - 1, "");
 		if (avrule->object_name)
 			fprintf(fp, " \"%s\"", avrule->object_name);
+		switch (avrule->name_match) {
+		case NAME_TRANS_MATCH_EXACT:
+			/* do nothing */
+			break;
+		case NAME_TRANS_MATCH_PREFIX:
+			fprintf(fp, " MATCH_PREFIX");
+			break;
+		case NAME_TRANS_MATCH_SUFFIX:
+			fprintf(fp, " MATCH_SUFFIX");
+			break;
+		default:
+			fprintf(fp, "     ERROR: no valid name match type specified\n");
+			return -1;
+		}
 	} else if (avrule->specified & AVRULE_XPERMS) {
 		avtab_extended_perms_t xperms;
 		int i;
diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
index 064cbd4e..87cd6a32 100644
--- a/checkpolicy/test/dispol.c
+++ b/checkpolicy/test/dispol.c
@@ -82,7 +82,7 @@ typedef struct {
 	avtab_key_t *key;
 	policydb_t *p;
 	FILE *fp;
-	name_trans_match_t match;
+	uint8_t match;
 } render_name_trans_args_t;
 
 static int render_name_trans_helper(hashtab_key_t k, hashtab_datum_t d, void *a)
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index 996bad70..ffa44be7 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -1211,7 +1211,8 @@ static int __cil_typetransition_to_avtab_helper(policydb_t *pdb,
 		avt_key.target_type = sepol_tgt->s.value;
 		avt_key.target_class = sepol_obj->s.value;
 		rc = avtab_insert_filename_trans(&pdb->te_avtab, &avt_key,
-			sepol_result->s.value, name, &otype);
+			sepol_result->s.value, name, NAME_TRANS_MATCH_EXACT,
+			&otype);
 		if (rc != SEPOL_OK) {
 			if (rc == SEPOL_EEXIST) {
 				if (sepol_result->s.value!= otype) {
@@ -4651,6 +4652,7 @@ static avrule_t *__cil_init_sepol_avrule(uint32_t kind, struct cil_tree_node *no
 	__cil_init_sepol_type_set(&avrule->ttypes);
 	avrule->perms = NULL;
 	avrule->object_name = NULL;
+	avrule->name_match = NAME_TRANS_MATCH_EXACT;
 	avrule->line = node->line;
 
 	avrule->source_filename = NULL;
diff --git a/libsepol/include/sepol/policydb/avtab.h b/libsepol/include/sepol/policydb/avtab.h
index 870fb08a..7d892879 100644
--- a/libsepol/include/sepol/policydb/avtab.h
+++ b/libsepol/include/sepol/policydb/avtab.h
@@ -156,6 +156,7 @@ extern avtab_ptr_t avtab_search_node_next(avtab_ptr_t node, int specified);
 
 extern int avtab_insert_filename_trans(avtab_t *a, avtab_key_t *key,
 				       uint32_t otype, const char *name,
+				       uint8_t name_match,
 				       uint32_t *present_otype);
 extern int avtab_filename_trans_read(void *fp, uint32_t vers, avtab_t *a);
 
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index a2df4a62..48b7b8bb 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -252,12 +252,6 @@ typedef struct av_extended_perms {
 	uint32_t perms[EXTENDED_PERMS_LEN];
 } av_extended_perms_t;
 
-typedef enum name_trans_match {
-	NAME_TRANS_MATCH_EXACT,
-	NAME_TRANS_MATCH_PREFIX,
-	NAME_TRANS_MATCH_SUFFIX,
-} name_trans_match_t;
-
 typedef struct avrule {
 /* these typedefs are almost exactly the same as those in avtab.h - they are
  * here because of the need to include neverallow and dontaudit messages */
@@ -285,6 +279,10 @@ typedef struct avrule {
 	type_set_t ttypes;
 	class_perm_node_t *perms;
 	char *object_name;	/* optional object name */
+#define NAME_TRANS_MATCH_EXACT 0
+#define NAME_TRANS_MATCH_PREFIX 1
+#define NAME_TRANS_MATCH_SUFFIX 2
+	uint8_t name_match;
 	av_extended_perms_t *xperms;
 	unsigned long line;	/* line number from policy.conf where
 				 * this rule originated  */
@@ -757,9 +755,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
 #define MOD_POLICYDB_VERSION_GLBLUB		20
 #define MOD_POLICYDB_VERSION_SELF_TYPETRANS	21
 #define MOD_POLICYDB_VERSION_AVRULE_FTRANS	22
+#define MOD_POLICYDB_VERSION_PREFIX_SUFFIX	23 /* preffix/suffix support for filename transitions */
 
 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE
-#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_AVRULE_FTRANS
+#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PREFIX_SUFFIX
 
 #define POLICYDB_CONFIG_MLS    1
 
diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 45cbb3a1..5dc7cc59 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -771,7 +771,7 @@ int avtab_read(avtab_t * a, struct policy_file *fp, uint32_t vers)
 
 int avtab_insert_filename_trans(avtab_t *a, avtab_key_t *key,
 				uint32_t otype, const char *name,
-				uint32_t *present_otype)
+				uint8_t name_match, uint32_t *present_otype)
 {
 	int rc = SEPOL_ENOMEM;
 	avtab_trans_t new_trans = {0};
@@ -780,6 +780,7 @@ int avtab_insert_filename_trans(avtab_t *a, avtab_key_t *key,
 	avtab_ptr_t node;
 	char *name_key = NULL;
 	uint32_t *otype_datum = NULL;
+	symtab_t *target_symtab;
 
 	datum = avtab_search(a, key);
 	if (!datum) {
@@ -793,8 +794,22 @@ int avtab_insert_filename_trans(avtab_t *a, avtab_key_t *key,
 		datum = &node->datum;
 	}
 
-	if (hashtab_is_empty(datum->trans->name_trans.table)) {
-		rc = symtab_init(&datum->trans->name_trans, 1 << 8);
+	switch (name_match) {
+	case NAME_TRANS_MATCH_EXACT:
+		target_symtab = &datum->trans->name_trans;
+		break;
+	case NAME_TRANS_MATCH_PREFIX:
+		target_symtab = &datum->trans->prefix_trans;
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		target_symtab = &datum->trans->suffix_trans;
+		break;
+	default:
+		return SEPOL_ERR;
+	}
+
+	if (hashtab_is_empty(target_symtab->table)) {
+		rc = symtab_init(target_symtab, 1 << 8);
 		if (rc < 0)
 			return rc;
 	}
@@ -808,8 +823,7 @@ int avtab_insert_filename_trans(avtab_t *a, avtab_key_t *key,
 		goto bad;
 	*otype_datum = otype;
 
-	rc = hashtab_insert(datum->trans->name_trans.table, name_key,
-			    otype_datum);
+	rc = hashtab_insert(target_symtab->table, name_key, otype_datum);
 	if (rc < 0)
 		goto bad;
 
@@ -854,7 +868,8 @@ static int filename_trans_read_one(avtab_t *a, void *fp)
 	key.target_class = le32_to_cpu(buf[2]);
 	otype = le32_to_cpu(buf[3]);
 
-	rc = avtab_insert_filename_trans(a, &key, otype, name, NULL);
+	rc = avtab_insert_filename_trans(a, &key, otype, name,
+					 NAME_TRANS_MATCH_EXACT, NULL);
 	if (rc)
 		goto err;
 
@@ -907,7 +922,8 @@ static int filename_trans_comp_read_one(avtab_t *a, void *fp)
 			key.source_type = bit + 1;
 
 			rc = avtab_insert_filename_trans(a, &key, otype, name,
-				NULL);
+							 NAME_TRANS_MATCH_EXACT,
+							 NULL);
 			if (rc < 0)
 				goto err_ebitmap;
 		}
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index f3417547..43f8ab3f 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -1620,7 +1620,8 @@ 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,
-				char *object_name, avtab_t * avtab, int enabled)
+				char *object_name, uint8_t name_match,
+				avtab_t * avtab, int enabled)
 {
 	avtab_key_t avkey;
 	avtab_datum_t *avdatump;
@@ -1652,6 +1653,7 @@ static int expand_terule_helper(sepol_handle_t * handle,
 			int rc = avtab_insert_filename_trans(avtab, &avkey,
 							     remapped_data,
 							     object_name,
+							     name_match,
 							     &oldtype);
 			if (rc == SEPOL_EEXIST) {
 				ERR(handle, "conflicting filename transition %s %s:%s \"%s\": %s vs %s",
@@ -1881,6 +1883,7 @@ static int expand_rule_helper(sepol_handle_t * handle,
 							      source_rule->specified, cond,
 							      other, i, i, source_rule->perms,
 							      source_rule->object_name,
+							      source_rule->name_match,
 							      dest_avtab, enabled);
 				if (retval != EXPAND_RULE_SUCCESS)
 					return retval;
@@ -1898,6 +1901,7 @@ static int expand_rule_helper(sepol_handle_t * handle,
 							      source_rule->specified, cond,
 							      other, i, j, source_rule->perms,
 							      source_rule->object_name,
+							      source_rule->name_match,
 							      dest_avtab, enabled);
 				if (retval != EXPAND_RULE_SUCCESS)
 					return retval;
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 2a1cae85..c0a72107 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -90,7 +90,7 @@ typedef struct {
 	const char *src;
 	const char *tgt;
 	const char *class;
-	name_trans_match_t match;
+	uint8_t match;
 } name_trans_to_strs_args_t;
 
 __attribute__ ((format(printf, 1, 2)))
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index 88b23594..332d62b2 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -1254,6 +1254,7 @@ static int copy_avrule_list(avrule_t * list, avrule_t ** dst,
 			if (!new_rule->object_name)
 				goto cleanup;
 		}
+		new_rule->name_match = cur->name_match;
 
 		cur_perm = cur->perms;
 		tail_perm = NULL;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 653b4b33..4b15bca1 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -560,12 +560,13 @@ 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 char *object_name, 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, uint8_t name_match, const struct class_perm_node *classperms)
 {
 	int rc = -1;
 	const char *rule;
 	const struct class_perm_node *classperm;
 	char *perms;
+	const char *match_str = "";
 
 	switch (type) {
 	case AVRULE_ALLOWED:
@@ -611,10 +612,24 @@ static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const
 					pdb->p_class_val_to_name[classperm->tclass - 1],
 					perms + 1);
 		} else if (object_name) {
-			cil_println(indent, "(%s %s %s %s \"%s\" %s)",
+			switch (name_match) {
+			case NAME_TRANS_MATCH_EXACT:
+				match_str = "";
+				break;
+			case NAME_TRANS_MATCH_PREFIX:
+				match_str = " match_prefix";
+				break;
+			case NAME_TRANS_MATCH_SUFFIX:
+				match_str = " match_suffix";
+				break;
+			default:
+				log_err("Unknown name match type: %" PRIu8,
+						name_match);
+			}
+			cil_println(indent, "(%s %s %s %s \"%s\"%s %s)",
 					rule, src, tgt,
 					pdb->p_class_val_to_name[classperm->tclass - 1],
-					object_name,
+					object_name, match_str,
 					pdb->p_type_val_to_name[classperm->data - 1]);
 		} else {
 			cil_println(indent, "(%s %s %s %s %s)",
@@ -1218,7 +1233,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->object_name, avrule->perms);
+					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->object_name, avrule->name_match, avrule->perms);
 				}
 				if (rc != 0) {
 					goto exit;
@@ -1229,7 +1244,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->object_name, avrule->perms);
+					rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->object_name, avrule->name_match, avrule->perms);
 				}
 				if (rc != 0) {
 					goto exit;
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index bd1a151d..594b875e 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -355,6 +355,13 @@ static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = OCON_IBENDPORT + 1,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_BASE,
+	 .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_IBENDPORT + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 	{
 	 .type = POLICY_MOD,
 	 .version = MOD_POLICYDB_VERSION_BASE,
@@ -488,6 +495,13 @@ static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = 0,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_MOD,
+	 .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = 0,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 };
 
 #if 0
@@ -3171,6 +3185,7 @@ common_read, class_read, role_read, type_read, user_read,
 static avrule_t *avrule_read(policydb_t * p, struct policy_file *fp)
 {
 	unsigned int i;
+	uint8_t buf8;
 	uint32_t buf[2], len;
 	class_perm_node_t *cur, *tail = NULL;
 	avrule_t *avrule;
@@ -3234,10 +3249,15 @@ static avrule_t *avrule_read(policydb_t * p, struct policy_file *fp)
 			if (rc < 0)
 				goto bad;
 		}
+		if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) {
+			rc = next_entry(&buf8, fp, sizeof(uint8_t));
+			if (rc < 0)
+				goto bad;
+			avrule->name_match = buf8;
+		}
 	}
 
 	if (avrule->specified & AVRULE_XPERMS) {
-		uint8_t buf8;
 		size_t nel = ARRAY_SIZE(avrule->xperms->perms);
 		uint32_t buf32[nel];
 
@@ -3546,6 +3566,7 @@ static int filename_trans_rule_read(policydb_t *p, avrule_t **r,
 		rc = str_read(&cur->object_name, fp, len);
 		if (rc)
 			return -1;
+		cur->name_match = NAME_TRANS_MATCH_EXACT;
 
 		if (type_set_read(&cur->stypes, fp))
 			return -1;
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 92a7b6cf..da83257d 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -2048,6 +2048,7 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
 			struct policy_file *fp)
 {
 	size_t items, items2;
+	uint8_t buf8;
 	uint32_t buf[32], len;
 	class_perm_node_t *cur;
 
@@ -2055,6 +2056,11 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
 	if (p->policyvers < MOD_POLICYDB_VERSION_AVRULE_FTRANS &&
 	    avrule->specified & AVRULE_TRANSITION && avrule->object_name)
 		return POLICYDB_SUCCESS;
+	/* skip prefix/suffix name transition if writing older version */
+	if (p->policyvers < MOD_POLICYDB_VERSION_PREFIX_SUFFIX &&
+	    avrule->specified & AVRULE_TRANSITION &&
+	    avrule->object_name && avrule->name_match != NAME_TRANS_MATCH_EXACT)
+		return POLICYDB_SUCCESS;
 
 	if (p->policyvers < MOD_POLICYDB_VERSION_SELF_TYPETRANS &&
 	    (avrule->specified & AVRULE_TYPE) &&
@@ -2113,12 +2119,17 @@ static int avrule_write(policydb_t *p, avrule_t * avrule,
 			if (items != len)
 				return POLICYDB_ERROR;
 		}
+		if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) {
+			buf8 = avrule->name_match;
+			items = put_entry(&buf8, sizeof(uint8_t), 1, fp);
+			if (items != 1)
+				return POLICYDB_ERROR;
+		}
 	}
 
 	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) {
@@ -2163,12 +2174,17 @@ static int avrule_write_list(policydb_t *p, avrule_t * avrules,
 
 	avrule = avrules;
 	len = 0;
-	while (avrule) {
-		if (p->policyvers >= MOD_POLICYDB_VERSION_AVRULE_FTRANS ||
-		    !(avrule->specified & AVRULE_TRANSITION &&
-		      avrule->object_name))
-			len++;
-		avrule = avrule->next;
+	for (avrule = avrules; avrule; avrule = avrule->next) {
+		if (p->policyvers < MOD_POLICYDB_VERSION_AVRULE_FTRANS &&
+		    (avrule->specified & AVTAB_TRANSITION) &&
+		    avrule->object_name)
+			continue;
+		if (p->policyvers < MOD_POLICYDB_VERSION_PREFIX_SUFFIX &&
+		    (avrule->specified & AVTAB_TRANSITION) &&
+		    avrule->object_name &&
+		    avrule->name_match != NAME_TRANS_MATCH_EXACT)
+			continue;
+		len++;
 	}
 
 	buf[0] = cpu_to_le32(len);
@@ -2276,7 +2292,8 @@ static int filename_trans_rule_write(policydb_t *p, avrule_t *rules,
 	class_perm_node_t *perm;
 
 	for (rule = rules; rule; rule = rule->next) {
-		if (rule->specified & AVRULE_TRANSITION && rule->object_name) {
+		if (rule->specified & AVRULE_TRANSITION && rule->object_name &&
+		    rule->name_match == NAME_TRANS_MATCH_EXACT) {
 			for (perm = rule->perms; perm; perm = perm->next) {
 				nel++;
 			}
@@ -2289,7 +2306,9 @@ static int filename_trans_rule_write(policydb_t *p, avrule_t *rules,
 		return POLICYDB_ERROR;
 
 	for (rule = rules; rule; rule = rule->next) {
-		if (!(rule->specified & AVRULE_TRANSITION && rule->object_name))
+		if (!(rule->specified & AVRULE_TRANSITION &&
+		      rule->object_name &&
+		      rule->name_match == NAME_TRANS_MATCH_EXACT))
 			continue;
 		len = strlen(rule->object_name);
 		for (perm = rule->perms; perm; perm = perm->next) {
@@ -2728,7 +2747,8 @@ int policydb_write(policydb_t * p, struct policy_file *fp)
 	if (p->policy_type == POLICY_KERN) {
 		if (avtab_write(p, &p->te_avtab, fp))
 			return POLICYDB_ERROR;
-		if (avtab_has_prefix_suffix_filename_transitions(&p->te_avtab)) {
+		if (p->policyvers < POLICYDB_VERSION_PREFIX_SUFFIX &&
+		    avtab_has_prefix_suffix_filename_transitions(&p->te_avtab)) {
 			WARN(fp->handle,
 			     "Discarding filename prefix/suffix type transition rules");
 		}
