Re: Patch to the context lookup change

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi there,
I recently made a change in android's selinux repo. The change
implements a new lookup helper function for context matches, and it
aims to skip unnecessary restorecon if the corresponding context
doesn't change.

Quote from the commit message. "We used to hash the file_context and
skip the restorecon on the top level directory if the hash doesn't
change. But the file_context might change after an OTA update; and
some users experienced long restorecon time as they have lots of files
under directories like /data/media. This CL tries to hash all the
partial match entries in the file_context for each directory; and
skips the restorecon if that digest stays the same, regardless of the
changes to the other parts of file_context."
CL in: https://android-review.googlesource.com/c/platform/external/selinux/+/918713

I haven't done the work in upstream code to fully implement this
change. But this CL merges and compiles. It factors out a lookup
helper function and returns an array of matched pointers instead of a
single one. The old loopup_common function is then modified to take
the first element in the array. I wonder if it's possible to merge in
this change to the lookup functions first? It will make the upstream &
android repo more consistent and also save some work for future
merges.
From f69947cffabefcda6b92212771bb6882678a0e81 Mon Sep 17 00:00:00 2001
From: xunchang <xunchang@google.com>
Date: Mon, 04 Mar 2019 22:12:42 -0800
Subject: [PATCH] Restorecon: factor out a lookup helper for context matches

This is part of the effort to save digest for subdirectories.
Split out the non-android part to make the merge to upstream
branch easier.

Bug: 62302954
Test: build android, compile the upstream branch
Change-Id: I4df94ed381f26356c539d604f31a65daabafc1da
---

diff --git a/libselinux/include/selinux/label.h b/libselinux/include/selinux/label.h
index 277287e..e537aa1 100644
--- a/libselinux/include/selinux/label.h
+++ b/libselinux/include/selinux/label.h
@@ -7,6 +7,7 @@
 #define _SELABEL_H_
 
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/types.h>
 #include <selinux/selinux.h>
 
@@ -105,6 +106,9 @@
 
 bool selabel_partial_match(struct selabel_handle *handle, const char *key);
 
+bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
+                                      const char *key, uint8_t* digest);
+
 int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
 			      const char *key, const char **aliases, int type);
 int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index ce786cd..e232eb1 100644
--- a/libselinux/src/label.c
+++ b/libselinux/src/label.c
@@ -282,6 +282,15 @@
 	return rec->func_partial_match(rec, key);
 }
 
+bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
+                                      const char *key, uint8_t *digest) {
+	if (!rec->func_hash_all_partial_matches) {
+		return false;
+	}
+
+	return rec->func_hash_all_partial_matches(rec, key, digest);
+}
+
 int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
 			      const char *key, const char **aliases, int type)
 {
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 6b64d66..fe43c4a 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -893,22 +893,37 @@
 	free(data);
 }
 
-static struct spec *lookup_common(struct selabel_handle *rec,
-					     const char *key,
-					     int type,
-					     bool partial)
+// Finds all the matches of |key| in the given context. Returns the result in
+// the allocated array and updates the match count. If match_count is NULL,
+// stops early once the 1st match is found.
+static const struct spec **lookup_all(struct selabel_handle *rec,
+				      const char *key,
+				      int type,
+				      bool partial,
+				      size_t *match_count)
 {
 	struct saved_data *data = (struct saved_data *)rec->data;
 	struct spec *spec_arr = data->spec_arr;
 	int i, rc, file_stem;
 	mode_t mode = (mode_t)type;
 	const char *buf;
-	struct spec *ret = NULL;
 	char *clean_key = NULL;
 	const char *prev_slash, *next_slash;
 	unsigned int sofar = 0;
 	char *sub = NULL;
 
+	const struct spec **result = NULL;
+	if (match_count) {
+		*match_count = 0;
+		result = calloc(data->nspec, sizeof(struct spec*));
+	} else {
+		result = calloc(1, sizeof(struct spec*));
+	}
+	if (!result) {
+		selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
+		goto finish;
+	}
+
 	if (!data->nspec) {
 		errno = ENOENT;
 		goto finish;
@@ -949,18 +964,33 @@
 		 * specified or if the mode matches the file mode then we do
 		 * a regex check        */
 		if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
-		    (!mode || !spec->mode || mode == spec->mode)) {
+				(!mode || !spec->mode || mode == spec->mode)) {
 			if (compile_regex(data, spec, NULL) < 0)
 				goto finish;
 			if (spec->stem_id == -1)
 				rc = regex_match(spec->regex, key, partial);
 			else
 				rc = regex_match(spec->regex, buf, partial);
-			if (rc == REGEX_MATCH) {
-				spec->matches++;
+
+			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
+				if (rc == REGEX_MATCH) {
+					spec->matches++;
+				}
+
+				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
+					errno = ENOENT;
+					goto finish;
+				}
+
+				if (match_count) {
+					result[*match_count] = spec;
+					*match_count += 1;
+					// Continue to find all the matches.
+					continue;
+				}
+				result[0] = spec;
 				break;
-			} else if (partial && rc == REGEX_MATCH_PARTIAL)
-				break;
+			}
 
 			if (rc == REGEX_NO_MATCH)
 				continue;
@@ -971,19 +1001,58 @@
 		}
 	}
 
-	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
-		/* No matching specification. */
-		errno = ENOENT;
-		goto finish;
-	}
-
-	errno = 0;
-	ret = &spec_arr[i];
-
 finish:
 	free(clean_key);
 	free(sub);
-	return ret;
+	if (result && !result[0]) {
+		free(result);
+		result = NULL;
+	}
+	return result;
+}
+
+static struct spec *lookup_common(struct selabel_handle *rec,
+				  const char *key,
+				  int type,
+				  bool partial) {
+	const struct spec **matches = lookup_all(rec, key, type, partial, NULL);
+	if (!matches) {
+		return NULL;
+	}
+	struct spec *result = (struct spec*)matches[0];
+	free(matches);
+	return result;
+}
+
+static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
+{
+	assert(digest);
+
+	size_t total_matches;
+	const struct spec **matches = lookup_all(rec, key, 0, true, &total_matches);
+	if (!matches) {
+		return false;
+	}
+
+	Sha1Context context;
+	Sha1Initialise(&context);
+	size_t i;
+	for (i = 0; i < total_matches; i++) {
+		char* regex_str = matches[i]->regex_str;
+		uint32_t mode = matches[i]->mode;
+		char* ctx_raw = matches[i]->lr.ctx_raw;
+
+		Sha1Update(&context, regex_str, strlen(regex_str) + 1);
+		Sha1Update(&context, &mode, sizeof(uint32_t));
+		Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
+	}
+
+	SHA1_HASH sha1_hash;
+	Sha1Finalise(&context, &sha1_hash);
+	memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
+
+	free(matches);
+	return true;
 }
 
 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
@@ -1183,6 +1252,7 @@
 	rec->func_stats = &stats;
 	rec->func_lookup = &lookup;
 	rec->func_partial_match = &partial_match;
+	rec->func_hash_all_partial_matches = &hash_all_partial_matches;
 	rec->func_lookup_best_match = &lookup_best_match;
 	rec->func_cmp = &cmp;
 
diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
index a05a10a..8add71a 100644
--- a/libselinux/src/label_internal.h
+++ b/libselinux/src/label_internal.h
@@ -87,6 +87,8 @@
 	void (*func_close) (struct selabel_handle *h);
 	void (*func_stats) (struct selabel_handle *h);
 	bool (*func_partial_match) (struct selabel_handle *h, const char *key);
+	bool (*func_hash_all_partial_matches) (struct selabel_handle *h,
+	                                       const char *key, uint8_t *digest);
 	struct selabel_lookup_rec *(*func_lookup_best_match)
 						    (struct selabel_handle *h,
 						    const char *key,

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux