Blob Blame History Raw
diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
index 6b9089d..85b0cfc 100644
--- a/libselinux/include/selinux/selinux.h
+++ b/libselinux/include/selinux/selinux.h
@@ -496,7 +496,9 @@ extern const char *selinux_policy_root(void);
 
 /* These functions return the paths to specific files under the 
    policy root directory. */
+extern const char *selinux_current_policy_path(void);
 extern const char *selinux_binary_policy_path(void);
+extern char *selinux_binary_policy_path_min_max(int min, int *max);
 extern const char *selinux_failsafe_context_path(void);
 extern const char *selinux_removable_context_path(void);
 extern const char *selinux_default_context_path(void);
diff --git a/libselinux/man/man3/selinux_binary_policy_path.3 b/libselinux/man/man3/selinux_binary_policy_path.3
index 8ead1a4..c68ace5 100644
--- a/libselinux/man/man3/selinux_binary_policy_path.3
+++ b/libselinux/man/man3/selinux_binary_policy_path.3
@@ -17,6 +17,8 @@ extern const char *selinux_policy_root(void);
 
 extern const char *selinux_binary_policy_path(void);
 
+extern const char *selinux_current_policy_path(void);
+
 extern const char *selinux_failsafe_context_path(void);
 
 extern const char *selinux_removable_context_path(void);
@@ -52,7 +54,9 @@ selinux_path() - top-level SELinux configuration directory
 .sp
 selinux_policy_root() - top-level policy directory 
 .sp
-selinux_binary_policy_path() - binary policy file loaded into kernel
+selinux_current_policy_path() - binary policy file loaded into kernel
+.sp
+selinux_binary_policy_path() - binary policy path on disk
 .sp
 selinux_default_type_path - context file mapping roles to default types.
 .sp
diff --git a/libselinux/src/audit2why.c b/libselinux/src/audit2why.c
index 02483a3..89953d7 100644
--- a/libselinux/src/audit2why.c
+++ b/libselinux/src/audit2why.c
@@ -206,27 +206,12 @@ static int __policy_init(const char *init_path)
 			return 1;
 		}
 	} else {
-		vers = sepol_policy_kern_vers_max();
-		if (vers < 0) {
-			snprintf(errormsg, sizeof(errormsg), 
-				 "Could not get policy version:  %s\n",
-				 strerror(errno));
-			PyErr_SetString( PyExc_ValueError, errormsg);
-			return 1;
-		}
-		snprintf(path, PATH_MAX, "%s.%d",
-			 selinux_binary_policy_path(), vers);
-		fp = fopen(path, "r");
-		while (!fp && errno == ENOENT && --vers) {
-			snprintf(path, PATH_MAX, "%s.%d",
-				 selinux_binary_policy_path(), vers);
-			fp = fopen(path, "r");
-		}
+		fp = fopen(selinux_current_policy_path(), "r");
 		if (!fp) {
 			snprintf(errormsg, sizeof(errormsg), 
-				 "unable to open %s.%d:  %s\n",
-				 selinux_binary_policy_path(),
-				 security_policyvers(), strerror(errno));
+				 "unable to open %s:  %s\n",
+				 selinux_current_policy_path(),
+				 strerror(errno));
 			PyErr_SetString( PyExc_ValueError, errormsg);
 			return 1;
 		}
diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c
index 802a07f..6ff83a7 100644
--- a/libselinux/src/avc.c
+++ b/libselinux/src/avc.c
@@ -827,6 +827,7 @@ int avc_has_perm(security_id_t ssid, security_id_t tsid,
 	errsave = errno;
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	errno = errsave;
+	if (!avc_enforcing) return 0;
 	return rc;
 }
 
diff --git a/libselinux/src/file_path_suffixes.h b/libselinux/src/file_path_suffixes.h
index 825f295..d11c8dc 100644
--- a/libselinux/src/file_path_suffixes.h
+++ b/libselinux/src/file_path_suffixes.h
@@ -26,4 +26,4 @@ S_(BINPOLICY, "/policy/policy")
     S_(FILE_CONTEXT_SUBS, "/contexts/files/file_contexts.subs")
     S_(FILE_CONTEXT_SUBS_DIST, "/contexts/files/file_contexts.subs_dist")
     S_(SEPGSQL_CONTEXTS, "/contexts/sepgsql_contexts")
-    S_(BOOLEAN_SUBS, "/booleans.subs")
+    S_(BOOLEAN_SUBS, "/booleans.subs_dist")
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 02b3cd2..fad8bbd 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -8,6 +8,7 @@
  * developed by Secure Computing Corporation.
  */
 
+#include <assert.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
@@ -16,7 +17,12 @@
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
+#include <stdint.h>
 #include <pcre.h>
+
+#include <linux/limits.h>
+
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -229,6 +235,167 @@ static int process_line(struct selabel_handle *rec,
 	return 0;
 }
 
+static int load_mmap(struct selabel_handle *rec, const char *path, struct stat *stat)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	char mmap_path[PATH_MAX + 1];
+	int mmapfd;
+	int rc, i;
+	struct stat mmap_stat;
+	char *addr;
+	size_t len;
+	int stem_map_len, *stem_map;
+
+	uint32_t *magic;
+	uint32_t *section_len;
+	uint32_t *plen;
+
+	rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
+	if (rc >= sizeof(mmap_path))
+		return -1;
+
+	mmapfd = open(mmap_path, O_RDONLY);
+	if (!mmapfd)
+		return -1;
+
+	rc = fstat(mmapfd, &mmap_stat);
+	if (rc < 0)
+		return -1;
+
+	/* if mmap is old, ignore it */
+	if (mmap_stat.st_mtime < stat->st_mtime)
+		return -1;
+
+	if (mmap_stat.st_mtime == stat->st_mtime &&
+	    mmap_stat.st_mtim.tv_nsec < stat->st_mtim.tv_nsec)
+		return -1;
+
+	/* ok, read it in... */
+	len = mmap_stat.st_size;
+	len += (sysconf(_SC_PAGE_SIZE) - 1);
+	len &= ~(sysconf(_SC_PAGE_SIZE) - 1);
+
+	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0);
+	close(mmapfd);
+	if (addr == MAP_FAILED) {
+		perror("mmap");
+		return -1;
+	}
+
+	/* check if this looks like an fcontext file */
+	magic = (uint32_t *)addr;
+	if (*magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
+		return -1;
+	addr += sizeof(uint32_t);
+
+	/* check if this version is higher than we understand */
+	section_len = (uint32_t *)addr;
+	if (*section_len > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
+		return -1;
+	addr += sizeof(uint32_t);
+
+	/* allocate the stems_data array */
+	section_len = (uint32_t *)addr;
+	addr += sizeof(uint32_t);
+
+	/*
+	 * map indexed by the stem # in the mmap file and contains the stem
+	 * number in the data stem_arr
+	 */
+	stem_map_len = *section_len;
+	stem_map = calloc(stem_map_len, sizeof(*stem_map));
+	if (!stem_map)
+		return -1;
+
+	for (i = 0; i < *section_len; i++) {
+		char *buf;
+		uint32_t stem_len;
+		int newid;
+
+		/* the length does not inlude the nul */
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+
+		stem_len = *plen;
+		buf = (char *)addr;
+		addr += (stem_len + 1); // +1 is the nul
+
+		/* store the mapping between old and new */
+		newid = find_stem(data, buf, stem_len);
+		if (newid < 0) {
+			newid = store_stem(data, buf, stem_len);
+			if (newid < 0)
+				return newid;
+			data->stem_arr[newid].from_mmap = 1;
+		}
+		stem_map[i] = newid;
+	}
+
+	/* allocate the regex array */
+	section_len = (uint32_t *)addr;
+	addr += sizeof(*section_len);
+
+	for (i = 0; i < *section_len; i++) {
+		struct spec *spec;
+		int32_t stem_id;
+
+		rc = grow_specs(data);
+		if (rc < 0)
+			return rc;
+
+		spec = &data->spec_arr[data->nspec];
+		spec->from_mmap = 1;
+		spec->regcomp = 1;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->lr.ctx_raw = strdup((char *)addr);
+		if (!spec->lr.ctx_raw)
+			return -1;
+		addr += *plen;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->regex_str = (char *)addr;
+		addr += *plen;
+
+		spec->mode = *(mode_t *)addr;
+		addr += sizeof(mode_t);
+
+		/* map the stem id from the mmap file to the data->stem_arr */
+		stem_id = *(int32_t *)addr;
+		if (stem_id == -1) {
+			spec->stem_id = -1;
+		} else {
+			assert(stem_id <= stem_map_len);
+			spec->stem_id = stem_map[stem_id];
+		}
+		addr += sizeof(int32_t);
+
+		/* retrieve the hasMetaChars bit */
+		spec->hasMetaChars = *(uint32_t *)addr;
+		addr += sizeof(uint32_t);
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->regex = (pcre *)addr;
+		addr += *plen;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->lsd.study_data = (void *)addr;
+		spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
+		addr += *plen;
+
+		data->nspec++;
+	}
+
+	free(stem_map);
+
+	/* win */
+	return 0;
+}
+
 static int process_file(const char *path, const char *suffix, struct selabel_handle *rec, const char *prefix)
 {
 	FILE *fp;
@@ -261,6 +428,10 @@ static int process_file(const char *path, const char *suffix, struct selabel_han
 		return -1;
 	}
 
+	rc = load_mmap(rec, path, &sb);
+	if (rc == 0)
+		goto out;
+
 	/*
 	 * The do detailed validation of the input and fill the spec array
 	 */
@@ -270,6 +441,7 @@ static int process_file(const char *path, const char *suffix, struct selabel_han
 		if (rc)
 			return rc;
 	}
+out:
 	free(line_buf);
 	fclose(fp);
 
@@ -357,6 +529,8 @@ static void closef(struct selabel_handle *rec)
 
 	for (i = 0; i < data->nspec; i++) {
 		spec = &data->spec_arr[i];
+		if (spec->from_mmap)
+			continue;
 		free(spec->regex_str);
 		free(spec->type_str);
 		free(spec->lr.ctx_raw);
@@ -369,6 +543,8 @@ static void closef(struct selabel_handle *rec)
 
 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
 		stem = &data->stem_arr[i];
+		if (stem->from_mmap)
+			continue;
 		free(stem->buf);
 	}
 
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index cb5633b..9799bbb 100644
--- a/libselinux/src/label_file.h
+++ b/libselinux/src/label_file.h
@@ -5,24 +5,32 @@
 
 #include "label_internal.h"
 
+#define SELINUX_MAGIC_COMPILED_FCONTEXT	0xf97cff8a
+#define SELINUX_COMPILED_FCONTEXT_MAX_VERS	1
+
 /* A file security context specification. */
 struct spec {
 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
 	char *regex_str;	/* regular expession string for diagnostics */
 	char *type_str;		/* type string for diagnostic messages */
 	pcre *regex;		/* compiled regular expression */
-	pcre_extra *sd;		/* extra compiled stuff */
+	union {
+		pcre_extra *sd;	/* pointer to extra compiled stuff */
+		pcre_extra lsd;	/* used to hold the mmap'd version */
+	};
 	mode_t mode;		/* mode format value */
 	int matches;		/* number of matching pathnames */
 	int stem_id;		/* indicates which stem-compression item */
 	char hasMetaChars;	/* regular expression has meta-chars */
 	char regcomp;		/* regex_str has been compiled to regex */
+	char from_mmap;		/* this spec is from an mmap of the data */
 };
 
 /* A regular expression stem */
 struct stem {
 	char *buf;
 	int len;
+	char from_mmap;
 };
 
 /* Our stored configuration */
@@ -45,7 +53,10 @@ struct saved_data {
 
 static inline pcre_extra *get_pcre_extra(struct spec *spec)
 {
-	return spec->sd;
+	if (spec->from_mmap)
+		return &spec->lsd;
+	else
+		return spec->sd;
 }
 
 static inline mode_t string_to_mode(char *mode)
diff --git a/libselinux/src/load_policy.c b/libselinux/src/load_policy.c
index 10e29b9..888dab5 100644
--- a/libselinux/src/load_policy.c
+++ b/libselinux/src/load_policy.c
@@ -49,8 +49,9 @@ int load_setlocaldefs hidden = 1;
 int selinux_mkload_policy(int preservebools)
 {	
 	int kernvers = security_policyvers();
-	int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION, vers;
+	int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION;
 	int setlocaldefs = load_setlocaldefs;
+	char *pol_path = NULL;
 	char path[PATH_MAX];
 	struct stat sb;
 	struct utsname uts;
@@ -162,29 +163,24 @@ checkbool:
 			maxvers = max(kernvers, maxvers);
 	}
 
-	vers = maxvers;
-      search:
-	snprintf(path, sizeof(path), "%s.%d",
-		 selinux_binary_policy_path(), vers);
-	fd = open(path, O_RDONLY);
-	while (fd < 0 && errno == ENOENT
-	       && --vers >= minvers) {
-		/* Check prior versions to see if old policy is available */
-		snprintf(path, sizeof(path), "%s.%d",
-			 selinux_binary_policy_path(), vers);
-		fd = open(path, O_RDONLY);
+search:
+	pol_path = selinux_binary_policy_path_min_max(minvers, &maxvers);
+	if (!pol_path) {
+		fprintf(stderr, "SELinux: unable to find usable policy file:  %s\n",
+			strerror(errno));
+		goto dlclose;
 	}
+
+	fd = open(pol_path, O_RDONLY);
 	if (fd < 0) {
-		fprintf(stderr,
-			"SELinux:  Could not open policy file <= %s.%d:  %s\n",
-			selinux_binary_policy_path(), maxvers, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not open policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto dlclose;
 	}
 
 	if (fstat(fd, &sb) < 0) {
-		fprintf(stderr,
-			"SELinux:  Could not stat policy file %s:  %s\n",
-			path, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not stat policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto close;
 	}
 
@@ -195,13 +191,12 @@ checkbool:
 	size = sb.st_size;
 	data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0);
 	if (map == MAP_FAILED) {
-		fprintf(stderr,
-			"SELinux:  Could not map policy file %s:  %s\n",
-			path, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not map policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto close;
 	}
 
-	if (vers > kernvers && usesepol) {
+	if (maxvers > kernvers && usesepol) {
 		/* Need to downgrade to kernel-supported version. */
 		if (policy_file_create(&pf))
 			goto unmap;
@@ -220,12 +215,12 @@ checkbool:
 			/* Downgrade failed, keep searching. */
 			fprintf(stderr,
 				"SELinux:  Could not downgrade policy file %s, searching for an older version.\n",
-				path);
+				pol_path);
 			policy_file_free(pf);
 			policydb_free(policydb);
 			munmap(map, sb.st_size);
 			close(fd);
-			vers--;
+			maxvers--;
 			goto search;
 		}
 		policy_file_free(pf);
@@ -281,7 +276,7 @@ checkbool:
 	if (rc)
 		fprintf(stderr,
 			"SELinux:  Could not load policy file %s:  %s\n",
-			path, strerror(errno));
+			pol_path, strerror(errno));
 
       unmap:
 	if (data != map)
@@ -296,6 +291,7 @@ checkbool:
 	if (libsepolh)
 		dlclose(libsepolh);
 #endif
+	free(pol_path);
 	return rc;
 }
 
diff --git a/libselinux/src/matchpathcon.c b/libselinux/src/matchpathcon.c
index 2d7369e..2a00807 100644
--- a/libselinux/src/matchpathcon.c
+++ b/libselinux/src/matchpathcon.c
@@ -2,6 +2,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
+#include <syslog.h>
 #include "selinux_internal.h"
 #include "label_internal.h"
 #include "callbacks.h"
@@ -62,7 +63,7 @@ static void
 {
 	va_list ap;
 	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
+	vsyslog(LOG_ERR, fmt, ap);
 	va_end(ap);
 }
 
diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c
index 296f357..cb65666 100644
--- a/libselinux/src/selinux_config.c
+++ b/libselinux/src/selinux_config.c
@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <pthread.h>
 #include "selinux_internal.h"
+#include "policy.h"
 #include "get_default_type_internal.h"
 
 #define SELINUXDIR "/etc/selinux/"
@@ -296,13 +297,57 @@ const char *selinux_removable_context_path(void)
 
 hidden_def(selinux_removable_context_path)
 
+char *selinux_binary_policy_path_min_max(int min, int *max)
+{
+	int ret;
+	char *path = NULL;
+
+	while(*max >= min) {
+		ret = asprintf(&path, "%s.%d", get_path(BINPOLICY), *max);
+		if (ret < 0)
+			goto err;
+		ret = access(path, R_OK);
+		if (!ret)
+			return path;
+		free(path);
+		path = NULL;
+		*max = *max - 1;
+	}
+err:
+	free(path);
+	return NULL;
+}
+hidden_def(selinux_binary_policy_path_min_max)
+
 const char *selinux_binary_policy_path(void)
 {
 	return get_path(BINPOLICY);
 }
-
 hidden_def(selinux_binary_policy_path)
 
+const char *selinux_current_policy_path(void)
+{
+	int rc = 0;
+	int vers = 0;
+	static char policy_path[PATH_MAX];
+
+	snprintf(policy_path, sizeof(policy_path), "%s/policy", selinux_mnt);
+	if (access(policy_path, F_OK) != 0 ) {
+		vers = security_policyvers();
+		do {
+			/* Check prior versions to see if old policy is available */
+			snprintf(policy_path, sizeof(policy_path), "%s.%d",
+				 selinux_binary_policy_path(), vers);
+		} while ((rc = access(policy_path, F_OK)) && --vers > 0);
+
+		if (rc) return NULL;
+	}
+
+	return policy_path;
+}
+
+hidden_def(selinux_current_policy_path)
+
 const char *selinux_file_context_path(void)
 {
 	return get_path(FILE_CONTEXTS);
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 2c7c85c..75ebc88 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -61,7 +61,9 @@ hidden_proto(selinux_mkload_policy)
     hidden_proto(security_deny_unknown)
     hidden_proto(selinux_boolean_sub)
     hidden_proto(selinux_binary_policy_path)
+    hidden_proto(selinux_binary_policy_path_min_max);
     hidden_proto(selinux_booleans_subs_path)
+    hidden_proto(selinux_current_policy_path)
     hidden_proto(selinux_default_context_path)
     hidden_proto(selinux_securetty_types_path)
     hidden_proto(selinux_failsafe_context_path)
diff --git a/libselinux/src/seusers.c b/libselinux/src/seusers.c
index cfea186..8b1eba6 100644
--- a/libselinux/src/seusers.c
+++ b/libselinux/src/seusers.c
@@ -125,7 +125,7 @@ static int check_group(const char *group, const char *name, const gid_t gid) {
 		rbuf = malloc(rbuflen);
 		if (rbuf == NULL)
 			return 0;
-		int retval = getgrnam_r(group, &gbuf, rbuf, 
+		int retval = getgrnam_r(group, &gbuf, rbuf,
 				rbuflen, &grent);
 		if ( retval == ERANGE )
 		{
@@ -198,13 +198,13 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level)
 		if (!strcmp(username, name))
 			break;
 
-		if (username[0] == '%' && 
-		    !groupseuser && 
+		if (username[0] == '%' &&
+		    !groupseuser &&
 		    check_group(&username[1], name, gid)) {
 				groupseuser = seuser;
 				grouplevel = level;
 		} else {
-			if (!defaultseuser && 
+			if (!defaultseuser &&
 			    !strcmp(username, "__default__")) {
 				defaultseuser = seuser;
 				defaultlevel = level;
@@ -258,7 +258,7 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level)
 	return 0;
 }
 
-int getseuser(const char *username, const char *service, 
+int getseuser(const char *username, const char *service,
 	      char **r_seuser, char **r_level) {
 	int ret = -1;
 	int len = 0;
diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
index 8b9294d..060eaab 100644
--- a/libselinux/utils/.gitignore
+++ b/libselinux/utils/.gitignore
@@ -13,6 +13,7 @@ getsebool
 getseuser
 matchpathcon
 policyvers
+sefcontext_compile
 selinux_check_securetty_context
 selinuxenabled
 selinuxexeccon
diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile
index 5f3e047..f469924 100644
--- a/libselinux/utils/Makefile
+++ b/libselinux/utils/Makefile
@@ -28,6 +28,7 @@ LDLIBS += -L../src -lselinux -L$(LIBDIR)
 
 TARGETS=$(patsubst %.c,%,$(wildcard *.c))
 
+sefcontext_compile: LDLIBS += -lpcre
 
 ifeq ($(DISABLE_AVC),y)
 	UNUSED_TARGETS+=compute_av compute_create compute_member compute_relabel
diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
new file mode 100644
index 0000000..f8a5fea
--- /dev/null
+++ b/libselinux/utils/sefcontext_compile.c
@@ -0,0 +1,345 @@
+#include <ctype.h>
+#include <errno.h>
+#include <pcre.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <linux/limits.h>
+
+#include "../src/label_file.h"
+
+static int process_file(struct saved_data *data, const char *filename)
+{
+	struct spec *spec;
+	unsigned int line_num;
+	char *line_buf = NULL;
+	size_t line_len;
+	ssize_t len;
+	FILE *context_file;
+
+	context_file = fopen(filename, "r");
+	if (!context_file)
+		return -1;
+
+	line_num = 0;
+	while ((len = getline(&line_buf, &line_len, context_file)) != -1) {
+		char *context;
+		char *mode;
+		char *regex;
+		char *cp, *anchored_regex;
+		char *buf_p;
+		pcre *re;
+		pcre_extra *sd;
+		const char *err;
+		int items, erroff, rc;
+		size_t regex_len;
+		int32_t stem_id;
+
+		len = strlen(line_buf);
+		if (line_buf[len - 1] == '\n')
+			line_buf[len - 1] = 0;
+		buf_p = line_buf;
+		while (isspace(*buf_p))
+			buf_p++;
+		/* Skip comment lines and empty lines. */
+		if (*buf_p == '#' || *buf_p == 0)
+			continue;
+
+		items = sscanf(line_buf, "%ms %ms %ms", &regex, &mode, &context);
+		if (items < 2 || items > 3) {
+			fprintf(stderr, "invalid entry, skipping:%s", line_buf);
+			continue;
+		}
+
+		if (items == 2) {
+			context = mode;
+			mode = NULL;
+		}
+
+		rc = grow_specs(data);
+		if (rc)
+			return rc;
+
+		spec = &data->spec_arr[data->nspec];
+
+		spec->lr.ctx_raw = context;
+		spec->mode = string_to_mode(mode);
+		if (spec->mode == -1) {
+			fprintf(stderr, "%s: line %d has invalid file type %s\n",
+				regex, line_num + 1, mode);
+			spec->mode = 0;
+		}
+		free(mode);
+		spec->regex_str = regex;
+
+		stem_id = find_stem_from_spec(data, regex);
+		spec->stem_id = stem_id;
+		/* skip past the fixed stem part */
+		if (stem_id != -1)
+			regex += data->stem_arr[stem_id].len;
+
+		regex_len = strlen(regex);
+		cp = anchored_regex = malloc(regex_len + 3);
+		if (!cp)
+			return -1;
+
+		*cp++ = '^';
+		memcpy(cp, regex, regex_len);
+		cp += regex_len;
+		*cp++ = '$';
+		*cp = '\0';
+
+		spec_hasMetaChars(spec);
+
+		re = pcre_compile(anchored_regex, 0, &err, &erroff, NULL);
+		if (!re) {
+			fprintf(stderr, "PCRE compilation failed for %s at offset %d: %s\n", anchored_regex, erroff, err);
+			return -1;
+		}
+		spec->regex = re;
+
+		sd = pcre_study(re, 0, &err);
+		if (!sd) {
+			fprintf(stderr, "PCRE study failed for %s: %s\n", anchored_regex, err);
+			return -1;
+		}
+		free(anchored_regex);
+		spec->sd = sd;
+
+		line_num++;
+		data->nspec++;
+	}
+
+	free(line_buf);
+	fclose(context_file);
+
+	return 0;
+}
+
+/*
+ * File Format
+ *
+ * u32 - magic number
+ * u32 - version
+ * u32 - number of stems
+ * ** Stems
+ * 	u32  - length of stem EXCLUDING nul
+ * 	char - stem char array INCLUDING nul
+ * u32 - number of regexs
+ * ** Regexes
+ * 	u32  - length of upcoming context INCLUDING nul
+ * 	char - char array of the raw context
+ *	u32  - length of the upcoming regex_str
+ *	char - char array of the original regex string including the stem.
+ *	mode_t - mode bits
+ *	s32  - stemid associated with the regex
+ *	u32  - spec has meta characters
+ *	u32  - data length of the pcre regex
+ *	char - a bufer holding the raw pcre regex info
+ *	u32  - data length of the pcre regex study daya
+ *	char - a buffer holding the raw pcre regex study data
+ */
+static int write_binary_file(struct saved_data *data, char *filename)
+{
+	struct spec *specs = data->spec_arr;
+	FILE *bin_file;
+	size_t len;
+	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
+	uint32_t section_len;
+	uint32_t i;
+
+	bin_file = fopen(filename, "w");
+	if (!bin_file) {
+		perror("fopen output_file");
+		exit(EXIT_FAILURE);
+	}
+
+	/* write some magic number */
+	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	/* write the version */
+	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	/* write the number of stems coming */
+	section_len = data->num_stems;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	for (i = 0; i < section_len; i++) {
+		char *stem = data->stem_arr[i].buf;
+		uint32_t stem_len = data->stem_arr[i].len;
+
+		/* write the strlen (aka no nul) */
+		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* include the nul in the file */
+		stem_len += 1;
+		len = fwrite(stem, sizeof(char), stem_len, bin_file);
+		if (len != stem_len)
+			return -1;
+	}
+
+	/* write the number of regexes coming */
+	section_len = data->nspec;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	for (i = 0; i < section_len; i++) {
+		char *context = specs[i].lr.ctx_raw;
+		char *regex_str = specs[i].regex_str;
+		mode_t mode = specs[i].mode;
+		int32_t stem_id = specs[i].stem_id;
+		pcre *re = specs[i].regex;
+		pcre_extra *sd = get_pcre_extra(&specs[i]);
+		uint32_t to_write;
+		size_t size;
+		int rc;
+
+		/* length of the context string (including nul) */
+		to_write = strlen(context) + 1;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* original context strin (including nul) */
+		len = fwrite(context, sizeof(char), to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* length of the original regex string (including nul) */
+		to_write = strlen(regex_str) + 1;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* original regex string */
+		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* binary F_MODE bits */
+		len = fwrite(&mode, sizeof(mode), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* stem for this regex (could be -1) */
+		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* does this spec have a metaChar? */
+		to_write = specs[i].hasMetaChars;
+		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* determine the size of the pcre data in bytes */
+		rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
+		if (rc < 0)
+			return -1;
+
+		/* write the number of bytes in the pcre data */
+		to_write = size;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* write the actual pcre data as a char array */
+		len = fwrite(re, 1, to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* determine the size of the pcre study info */
+		rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
+		if (rc < 0)
+			return -1;
+
+		/* write the number of bytes in the pcre study data */
+		to_write = size;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* write the actual pcre study data as a char array */
+		len = fwrite(sd->study_data, 1, to_write, bin_file);
+		if (len != to_write)
+			return -1;
+	}
+
+	fclose(bin_file);
+
+	return 0;
+}
+
+static int free_specs(struct saved_data *data)
+{
+	struct spec *specs = data->spec_arr;
+	unsigned int num_entries = data->nspec;
+	unsigned int i;
+
+	for (i = 0; i < num_entries; i++) {
+		free(specs[i].lr.ctx_raw);
+		free(specs[i].lr.ctx_trans);
+		free(specs[i].regex_str);
+		pcre_free(specs[i].regex);
+		pcre_free_study(specs[i].sd);
+	}
+	free(specs);
+
+	num_entries = data->num_stems;
+	for (i = 0; i < num_entries; i++) {
+		free(data->stem_arr[i].buf);
+	}
+	free(data->stem_arr);
+
+	memset(data, 0, sizeof(*data));
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct saved_data data;
+	const char *path;
+	char stack_path[PATH_MAX + 1];
+	int rc;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s input_file\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	path = argv[1];
+
+	rc = process_file(&data, path);
+	if (rc < 0)
+		return rc;
+
+	rc = sort_specs(&data);
+	if (rc)
+		return rc;
+
+	rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
+	if (rc < 0 || rc >= sizeof(stack_path))
+		return rc;
+	rc = write_binary_file(&data, stack_path);
+	if (rc < 0)
+		return rc;
+
+	rc = free_specs(&data);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}