am 09fbb966: (-s ours) am be86166f: restorecon: do not set security.restorecon_last on ramfs and tmpfs

* commit '09fbb9669af14fd9bb97234ec518f8ac91819368':
diff --git a/include/selinux/label.h b/include/selinux/label.h
index a7e91b7..512c71f 100644
--- a/include/selinux/label.h
+++ b/include/selinux/label.h
@@ -104,6 +104,26 @@
 int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
 			      const char *key, const char **aliases, int type);
 
+enum selabel_cmp_result {
+	SELABEL_SUBSET,
+	SELABEL_EQUAL,
+	SELABEL_SUPERSET,
+	SELABEL_INCOMPARABLE
+};
+
+/**
+ * selabel_cmp - Compare two label configurations.
+ * @h1: handle for the first label configuration
+ * @h2: handle for the first label configuration
+ *
+ * Compare two label configurations.
+ * Return %SELABEL_SUBSET if @h1 is a subset of @h2, %SELABEL_EQUAL
+ * if @h1 is identical to @h2, %SELABEL_SUPERSET if @h1 is a superset
+ * of @h2, and %SELABEL_INCOMPARABLE if @h1 and @h2 are incomparable.
+ */
+enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
+				    struct selabel_handle *h2);
+
 /**
  * selabel_stats - log labeling operation statistics.
  * @handle: specifies backend instance to query
diff --git a/src/android.c b/src/android.c
index 5608f19..8f66a5a 100644
--- a/src/android.c
+++ b/src/android.c
@@ -44,8 +44,8 @@
 	NULL };
 
 static const struct selinux_opt seopts[] = {
-	{ SELABEL_OPT_PATH, "/file_contexts" },
-	{ SELABEL_OPT_PATH, "/data/security/current/file_contexts" },
+	{ SELABEL_OPT_PATH, "/file_contexts.bin" },
+	{ SELABEL_OPT_PATH, "/data/security/current/file_contexts.bin" },
 	{ 0, NULL } };
 
 static const char *const sepolicy_file[] = {
@@ -1383,10 +1383,17 @@
 
 struct selabel_handle* selinux_android_file_context_handle(void)
 {
+    char *path = NULL;
     struct selabel_handle *sehandle;
+    struct selinux_opt fc_opts[] = {
+        { SELABEL_OPT_PATH, path },
+        { SELABEL_OPT_BASEONLY, (char *)1 }
+    };
 
     set_policy_index();
-    sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[policy_index], 1);
+    fc_opts[0].value = seopts[policy_index].value;
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 2);
 
     if (!sehandle) {
         selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
@@ -1398,7 +1405,7 @@
         return NULL;
     }
     selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts contexts from %s.\n",
-            seopts[policy_index].value);
+            fc_opts[0].value);
 
     return sehandle;
 }
diff --git a/src/label.c b/src/label.c
index dd51aa3..84cee51 100644
--- a/src/label.c
+++ b/src/label.c
@@ -149,6 +149,15 @@
 	return *con ? 0 : -1;
 }
 
+enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
+				    struct selabel_handle *h2)
+{
+	if (!h1->func_cmp || h1->func_cmp != h2->func_cmp)
+		return SELABEL_INCOMPARABLE;
+
+	return h1->func_cmp(h1, h2);
+}
+
 void selabel_close(struct selabel_handle *rec)
 {
 	rec->func_close(rec);
diff --git a/src/label_android_property.c b/src/label_android_property.c
index 4af9896..af06c4a 100644
--- a/src/label_android_property.c
+++ b/src/label_android_property.c
@@ -94,11 +94,12 @@
 	if (items <= 0)
 		return items;
 	if (items != 2) {
-		selinux_log(SELINUX_WARNING,
-			    "%s:  line %u is missing fields, skipping\n", path,
+		selinux_log(SELINUX_ERROR,
+			    "%s:  line %u is missing fields\n", path,
 			    lineno);
 		free(prop);
-		return 0;
+		errno = EINVAL;
+		return -1;
 	}
 
 	if (pass == 0) {
@@ -107,26 +108,15 @@
 	} else if (pass == 1) {
 		/* On the second pass, process and store the specification in spec. */
 		spec_arr[nspec].property_key = prop;
-		if (!spec_arr[nspec].property_key) {
-			selinux_log(SELINUX_WARNING,
-				    "%s:  out of memory at line %u on prop %s\n",
-				    path, lineno, prop);
-			return -1;
-		}
-
 		spec_arr[nspec].lr.ctx_raw = context;
-		if (!spec_arr[nspec].lr.ctx_raw) {
-			selinux_log(SELINUX_WARNING,
-				    "%s:  out of memory at line %u on context %s\n",
-				    path, lineno, context);
-			return -1;
-		}
 
 		if (rec->validating) {
 			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
-				selinux_log(SELINUX_WARNING,
+				selinux_log(SELINUX_ERROR,
 					    "%s:  line %u has invalid context %s\n",
 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
+				errno = EINVAL;
+				return -1;
 			}
 		}
 	}
diff --git a/src/label_file.c b/src/label_file.c
index 54931d7..f710ab4 100644
--- a/src/label_file.c
+++ b/src/label_file.c
@@ -5,6 +5,7 @@
  * Author : Stephen Smalley <sds@tycho.nsa.gov>
  */
 
+#include <assert.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
@@ -96,7 +97,7 @@
 }
 
 static int load_mmap(struct selabel_handle *rec, const char *path,
-						    struct stat *sb)
+		     struct stat *sb, bool isbinary)
 {
 	struct saved_data *data = (struct saved_data *)rec->data;
 	char mmap_path[PATH_MAX + 1];
@@ -110,9 +111,16 @@
 	uint32_t i, magic, version;
 	uint32_t entry_len, stem_map_len, regex_array_len;
 
-	rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
-	if (rc >= (int)sizeof(mmap_path))
-		return -1;
+	if (isbinary) {
+		len = strlen(path);
+		if (len >= sizeof(mmap_path))
+			return -1;
+		strcpy(mmap_path, path);
+	} else {
+		rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
+		if (rc >= (int)sizeof(mmap_path))
+			return -1;
+	}
 
 	mmapfd = open(mmap_path, O_RDONLY | O_CLOEXEC);
 	if (mmapfd < 0)
@@ -293,6 +301,14 @@
 		}
 		spec->lr.ctx_raw = str_buf;
 
+		if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) {
+			if (selabel_validate(rec, &spec->lr) < 0) {
+				selinux_log(SELINUX_ERROR,
+					    "%s: context %s is invalid\n", mmap_path, spec->lr.ctx_raw);
+				goto err;
+			}
+		}
+
 		/* Process regex string */
 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
 		if (rc < 0 || !entry_len) {
@@ -404,6 +420,8 @@
 	char *line_buf = NULL;
 	int rc;
 	char stack_path[PATH_MAX + 1];
+	bool isbinary = false;
+	uint32_t magic;
 
 	/* append the path suffix if we have one */
 	if (suffix) {
@@ -417,20 +435,45 @@
 	}
 
 	/* Open the specification file. */
-	if ((fp = fopen(path, "r")) == NULL)
-		return -1;
+	fp = fopen(path, "r");
+	if (fp) {
+		if (fstat(fileno(fp), &sb) < 0)
+			return -1;
+		if (!S_ISREG(sb.st_mode)) {
+			errno = EINVAL;
+			return -1;
+		}
 
-	if (fstat(fileno(fp), &sb) < 0)
-		return -1;
-	if (!S_ISREG(sb.st_mode)) {
-		errno = EINVAL;
-		return -1;
+		if (fread(&magic, sizeof magic, 1, fp) != 1) {
+			errno = EINVAL;
+			fclose(fp);
+			return -1;
+		}
+
+		if (magic == SELINUX_MAGIC_COMPILED_FCONTEXT) {
+			/* file_contexts.bin format */
+			fclose(fp);
+			fp = NULL;
+			isbinary = true;
+		} else {
+			rewind(fp);
+		}
+	} else {
+		/*
+		 * Text file does not exist, so clear the timestamp
+		 * so that we will always pass the timestamp comparison
+		 * with the bin file in load_mmap().
+		 */
+		sb.st_mtime = 0;
 	}
 
-	rc = load_mmap(rec, path, &sb);
+	rc = load_mmap(rec, path, &sb, isbinary);
 	if (rc == 0)
 		goto out;
 
+	if (!fp)
+		return -1; /* no text or bin file */
+
 	/*
 	 * Then do detailed validation of the input and fill the spec array
 	 */
@@ -444,7 +487,8 @@
 
 out:
 	free(line_buf);
-	fclose(fp);
+	if (fp)
+		fclose(fp);
 	return rc;
 }
 
@@ -719,6 +763,96 @@
 	return lr;
 }
 
+static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j)
+{
+	selinux_log(SELINUX_INFO,
+		    "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
+		    reason,
+		    i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
+		    j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
+	return SELABEL_INCOMPARABLE;
+}
+
+static enum selabel_cmp_result cmp(struct selabel_handle *h1,
+				   struct selabel_handle *h2)
+{
+	struct saved_data *data1 = (struct saved_data *)h1->data;
+	struct saved_data *data2 = (struct saved_data *)h2->data;
+	unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
+	struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
+	struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
+	bool skipped1 = false, skipped2 = false;
+
+	i = 0;
+	j = 0;
+	while (i < nspec1 && j < nspec2) {
+		struct spec *spec1 = &spec_arr1[i];
+		struct spec *spec2 = &spec_arr2[j];
+
+		/*
+		 * Because sort_specs() moves exact pathnames to the
+		 * end, we might need to skip over additional regex
+		 * entries that only exist in one of the configurations.
+		 */
+		if (!spec1->hasMetaChars && spec2->hasMetaChars) {
+			j++;
+			skipped2 = true;
+			continue;
+		}
+
+		if (spec1->hasMetaChars && !spec2->hasMetaChars) {
+			i++;
+			skipped1 = true;
+			continue;
+		}
+
+		if (spec1->regcomp && spec2->regcomp) {
+			size_t len1, len2;
+			int rc;
+
+			rc = pcre_fullinfo(spec1->regex, NULL, PCRE_INFO_SIZE, &len1);
+			assert(rc == 0);
+			rc = pcre_fullinfo(spec2->regex, NULL, PCRE_INFO_SIZE, &len2);
+			assert(rc == 0);
+			if (len1 != len2 ||
+			    memcmp(spec1->regex, spec2->regex, len1))
+				return incomp(spec1, spec2, "regex", i, j);
+		} else {
+			if (strcmp(spec1->regex_str, spec2->regex_str))
+				return incomp(spec1, spec2, "regex_str", i, j);
+		}
+
+		if (spec1->mode != spec2->mode)
+			return incomp(spec1, spec2, "mode", i, j);
+
+		if (spec1->stem_id == -1 && spec2->stem_id != -1)
+			return incomp(spec1, spec2, "stem_id", i, j);
+		if (spec2->stem_id == -1 && spec1->stem_id != -1)
+			return incomp(spec1, spec2, "stem_id", i, j);
+		if (spec1->stem_id != -1 && spec2->stem_id != -1) {
+			struct stem *stem1 = &stem_arr1[spec1->stem_id];
+			struct stem *stem2 = &stem_arr2[spec2->stem_id];
+			if (stem1->len != stem2->len ||
+			    strncmp(stem1->buf, stem2->buf, stem1->len))
+				return incomp(spec1, spec2, "stem", i, j);
+		}
+
+		if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
+			return incomp(spec1, spec2, "ctx_raw", i, j);
+
+		i++;
+		j++;
+	}
+
+	if ((skipped1 || i < nspec1) && !skipped2)
+		return SELABEL_SUPERSET;
+	if ((skipped2 || j < nspec2) && !skipped1)
+		return SELABEL_SUBSET;
+	if (skipped1 && skipped2)
+		return SELABEL_INCOMPARABLE;
+	return SELABEL_EQUAL;
+}
+
 
 static void stats(struct selabel_handle *rec)
 {
@@ -760,6 +894,7 @@
 	rec->func_lookup = &lookup;
 	rec->func_partial_match = &partial_match;
 	rec->func_lookup_best_match = &lookup_best_match;
+	rec->func_cmp = &cmp;
 
 	return init(rec, opts, nopts);
 }
diff --git a/src/label_file.h b/src/label_file.h
index 3f19394..678f07c 100644
--- a/src/label_file.h
+++ b/src/label_file.h
@@ -392,12 +392,13 @@
 		return items;
 
 	if (items < 2) {
-		selinux_log(SELINUX_WARNING,
-			    "%s:  line %u is missing fields, skipping\n", path,
+		selinux_log(SELINUX_ERROR,
+			    "%s:  line %u is missing fields\n", path,
 			    lineno);
 		if (items == 1)
 			free(regex);
-		return 0;
+		errno = EINVAL;
+		return -1;
 	} else if (items == 2) {
 		/* The type field is optional. */
 		context = type;
@@ -424,10 +425,12 @@
 	spec_arr[nspec].regex_str = regex;
 	if (rec->validating &&
 			    compile_regex(data, &spec_arr[nspec], &errbuf)) {
-		selinux_log(SELINUX_WARNING,
+		selinux_log(SELINUX_ERROR,
 			   "%s:  line %u has invalid regex %s:  %s\n",
 			   path, lineno, regex,
 			   (errbuf ? errbuf : "out of memory"));
+		errno = EINVAL;
+		return -1;
 	}
 
 	/* Convert the type string to a mode format */
@@ -437,10 +440,11 @@
 		mode_t mode = string_to_mode(type);
 
 		if (mode == (mode_t)-1) {
-			selinux_log(SELINUX_WARNING,
+			selinux_log(SELINUX_ERROR,
 				   "%s:  line %u has invalid file type %s\n",
 				   path, lineno, type);
-			mode = 0;
+			errno = EINVAL;
+			return -1;
 		}
 		spec_arr[nspec].mode = mode;
 	}
@@ -453,9 +457,11 @@
 
 	if (strcmp(context, "<<none>>") && rec->validating) {
 		if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
-			selinux_log(SELINUX_WARNING,
+			selinux_log(SELINUX_ERROR,
 				    "%s:  line %u has invalid context %s\n",
 				    path, lineno, spec_arr[nspec].lr.ctx_raw);
+			errno = EINVAL;
+			return -1;
 		}
 	}
 
diff --git a/src/label_internal.h b/src/label_internal.h
index 0c21e36..0e13833 100644
--- a/src/label_internal.h
+++ b/src/label_internal.h
@@ -60,6 +60,8 @@
 						    const char *key,
 						    const char **aliases,
 						    int type);
+	enum selabel_cmp_result (*func_cmp)(struct selabel_handle *h1,
+					    struct selabel_handle *h2);
 
 	/* supports backend-specific state information */
 	void *data;