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,