* [PATCH v3 1/2] libselinux: support multiple spec_files @ 2026-07-27 1:39 Thiébaud Weksteen 2026-07-27 1:39 ` [PATCH v3 2/2] libselinux: support multiple context files for the file backend Thiébaud Weksteen 2026-07-27 13:30 ` [PATCH v3 1/2] libselinux: support multiple spec_files Stephen Smalley 0 siblings, 2 replies; 4+ messages in thread From: Thiébaud Weksteen @ 2026-07-27 1:39 UTC (permalink / raw) To: selinux, Stephen Smalley Cc: James Carter, Christian Göttsche, Ondrej Mosnacek, Thiébaud Weksteen Update the existing backends to potentially allocate multiple spec_files. This commit simply adds a level of indirection but does not change any current behaviour. It will facilitate the gradual migration of backends to multiple context files. In selabel_fini(), compat_validate() is updated to use the first spec_file only, for legacy validation error reporting. selabel_open() is already performing context validation if requested against individual file paths (see insert_spec). Signed-off-by: Thiébaud Weksteen <tweek@google.com> --- No changes since v2 libselinux/src/label.c | 10 ++++++++-- libselinux/src/label_db.c | 11 +++++++++-- libselinux/src/label_file.c | 8 ++++++-- libselinux/src/label_internal.h | 5 +++-- libselinux/src/label_media.c | 8 +++++++- libselinux/src/label_x.c | 8 +++++++- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/libselinux/src/label.c b/libselinux/src/label.c index 991b9769..4a97d3bc 100644 --- a/libselinux/src/label.c +++ b/libselinux/src/label.c @@ -162,7 +162,7 @@ static int selabel_fini(const struct selabel_handle *rec, char *ctx_trans; int rc; - if (compat_validate(rec, lr, rec->spec_file, lr->lineno)) + if (compat_validate(rec, lr, rec->spec_files[0], lr->lineno)) return -1; if (!translating) @@ -398,11 +398,17 @@ int selabel_digest(struct selabel_handle *rec, unsigned char **digest, void selabel_close(struct selabel_handle *rec) { + size_t i; + if (rec->digest) selabel_digest_fini(rec->digest); if (rec->func_close) rec->func_close(rec); - free(rec->spec_file); + if (rec->spec_files) { + for (i = 0; i < rec->spec_files_len; i++) + free(rec->spec_files[i]); + free(rec->spec_files); + } free(rec); } diff --git a/libselinux/src/label_db.c b/libselinux/src/label_db.c index bafa9328..53731470 100644 --- a/libselinux/src/label_db.c +++ b/libselinux/src/label_db.c @@ -302,12 +302,19 @@ static catalog_t *db_init(const struct selinux_opt *opts, unsigned nopts, errno = EINVAL; return NULL; } - rec->spec_file = strdup(path); - if (!rec->spec_file) { + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); + if (!rec->spec_files) { free(catalog); fclose(filp); return NULL; } + rec->spec_files[0] = strdup(path); + if (!rec->spec_files[0]) { + free(catalog); + fclose(filp); + return NULL; + } + rec->spec_files_len = 1; /* * Parse for each lines diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index 0c0499eb..d91e1462 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -1549,9 +1549,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, goto finish; } - rec->spec_file = strdup(path); - if (!rec->spec_file) + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); + if (!rec->spec_files) goto finish; + rec->spec_files[0] = strdup(path); + if (!rec->spec_files[0]) + goto finish; + rec->spec_files_len = 1; /* * The do detailed validation of the input and fill the spec array diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h index 4ff39d96..d54053df 100644 --- a/libselinux/src/label_internal.h +++ b/libselinux/src/label_internal.h @@ -96,10 +96,11 @@ struct selabel_handle { void *data; /* - * The main spec file used. Note for file contexts the local and/or + * The spec files used. Note for file contexts the local and/or * homedirs could also have been used to resolve a context. */ - char *spec_file; + size_t spec_files_len; + char **spec_files; /* ptr to SHA1 hash information if SELABEL_OPT_DIGEST set */ struct selabel_digest *digest; diff --git a/libselinux/src/label_media.c b/libselinux/src/label_media.c index 957fcfd3..f315abc3 100644 --- a/libselinux/src/label_media.c +++ b/libselinux/src/label_media.c @@ -109,7 +109,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, errno = EINVAL; goto finish; } - rec->spec_file = strdup(path); + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); + if (!rec->spec_files) + goto finish; + rec->spec_files[0] = strdup(path); + if (!rec->spec_files[0]) + goto finish; + rec->spec_files_len = 1; /* * Perform two passes over the specification file. diff --git a/libselinux/src/label_x.c b/libselinux/src/label_x.c index 0c525bfc..b528a019 100644 --- a/libselinux/src/label_x.c +++ b/libselinux/src/label_x.c @@ -139,7 +139,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, errno = EINVAL; goto finish; } - rec->spec_file = strdup(path); + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); + if (!rec->spec_files) + goto finish; + rec->spec_files[0] = strdup(path); + if (!rec->spec_files[0]) + goto finish; + rec->spec_files_len = 1; /* * Perform two passes over the specification file. -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v3 2/2] libselinux: support multiple context files for the file backend 2026-07-27 1:39 [PATCH v3 1/2] libselinux: support multiple spec_files Thiébaud Weksteen @ 2026-07-27 1:39 ` Thiébaud Weksteen 2026-07-27 13:30 ` Stephen Smalley 2026-07-27 13:30 ` [PATCH v3 1/2] libselinux: support multiple spec_files Stephen Smalley 1 sibling, 1 reply; 4+ messages in thread From: Thiébaud Weksteen @ 2026-07-27 1:39 UTC (permalink / raw) To: selinux, Stephen Smalley Cc: James Carter, Christian Göttsche, Ondrej Mosnacek, Thiébaud Weksteen Update the file labeling backend to support specifying multiple SELABEL_OPT_PATH options in selabel_open(). All provided paths are stored in rec->spec_files and processed during initialization. Derived files, such as substitutions (.subs, .subs_dist) and auxiliary contexts (.homedirs, .local), continue to be based on the first path (or default selinux_file_context_path()) when enabled. Duplicate checking is only performed within each context file. The current behavior expects the ability for a later file to overwrite a previous declaration (e.g., .local). Signed-off-by: Thiébaud Weksteen <tweek@google.com> --- Changes since v2: - Add optional paths (homedirs, local) to spec_files. Since these might be referenced, let's keep track of them as any other spec files. spec_files now contains the required files followed by the optional files. - Strictly validate that, at most, 255 options are provided to init(). - Remove the intermediate `path` variable. - Note: the 2rd argument of process_file is now always NULL. This can be cleaned in a follow up commit. Changes since v1: - Restore nodups_spec_node, ignore duplicates across files. - Change num_paths type to uint8_t and check for overflow. - Check for NULL before strdup any path. - Use num_paths and num_paths+1 for homedirs and local. libselinux/src/label_file.c | 154 ++++++++++++++++++++++---------- libselinux/src/label_internal.h | 3 +- 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index d91e1462..c955216b 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -106,7 +106,8 @@ void sort_spec_node(struct spec_node *node, struct spec_node *parent) /* * Warn about duplicate specifications. */ -static int nodups_spec_node(const struct spec_node *node, const char *path) +static int nodups_spec_node(const struct selabel_handle *rec, + const struct spec_node *node) { int rc = 0; @@ -126,13 +127,16 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) node1->file_kind != node2->file_kind) continue; + if (node1->inputno != node2->inputno) + continue; + rc = -1; errno = EINVAL; if (strcmp(node1->lr.ctx_raw, node2->lr.ctx_raw) != 0) { COMPAT_LOG( SELINUX_ERROR, "%s: Multiple different specifications for %s %s (%s and %s).\n", - path, + rec->spec_files[node1->inputno], file_kind_to_string(node1->file_kind), node1->literal_match, node1->lr.ctx_raw, node2->lr.ctx_raw); @@ -140,7 +144,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) COMPAT_LOG( SELINUX_ERROR, "%s: Multiple same specifications for %s %s.\n", - path, + rec->spec_files[node1->inputno], file_kind_to_string(node1->file_kind), node1->literal_match); } @@ -168,6 +172,9 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) node1->file_kind != node2->file_kind) continue; + if (node1->inputno != node2->inputno) + continue; + rc = -1; errno = EINVAL; if (strcmp(node1->lr.ctx_raw, @@ -175,7 +182,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) COMPAT_LOG( SELINUX_ERROR, "%s: Multiple different specifications for %s %s (%s and %s).\n", - path, + rec->spec_files[node1->inputno], file_kind_to_string( node1->file_kind), node1->regex_str, @@ -185,7 +192,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) COMPAT_LOG( SELINUX_ERROR, "%s: Multiple same specifications for %s %s.\n", - path, + rec->spec_files[node1->inputno], file_kind_to_string( node1->file_kind), node1->regex_str); @@ -197,7 +204,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) for (uint32_t i = 0; i < node->children_num; i++) { int rc2; - rc2 = nodups_spec_node(&node->children[i], path); + rc2 = nodups_spec_node(rec, &node->children[i]); if (rc2) rc = rc2; } @@ -1480,26 +1487,37 @@ static char *selabel_sub_key(const struct saved_data *data, const char *key, static void closef(struct selabel_handle *rec); +static const char *const opt_suffixes[] = { "homedirs", "local" }; + static int init(struct selabel_handle *rec, const struct selinux_opt *opts, unsigned n) { struct saved_data *data = rec->data; - const char *path = NULL; + uint8_t num_required_paths = 0, num_optional_paths = 0, i, j; + size_t total_paths; const char *prefix = NULL; - int status = -1, baseonly = 0; + int status = -1; + bool baseonly = false, path_provided = false; + + if (n > UINT8_MAX) { + errno = EINVAL; + return -1; + } /* Process arguments */ - while (n) { - n--; - switch (opts[n].type) { + for (i = 0; i < n; i++) { + switch (opts[i].type) { case SELABEL_OPT_PATH: - path = opts[n].value; + if (opts[i].value) { + num_required_paths++; + path_provided = true; + } break; case SELABEL_OPT_SUBSET: - prefix = opts[n].value; + prefix = opts[i].value; break; case SELABEL_OPT_BASEONLY: - baseonly = !!opts[n].value; + baseonly = !!opts[i].value; break; case SELABEL_OPT_UNUSED: case SELABEL_OPT_VALIDATE: @@ -1511,10 +1529,56 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, } } + /* If no paths were provided, we will use the default path or fail, + * depending on the target. */ + if (!path_provided) { +#if !defined(BUILD_HOST) && !defined(ANDROID) + num_required_paths = 1; +#else + selinux_log(SELINUX_ERROR, + "No path given to file labeling backend\n"); + errno = EINVAL; + return -1; +#endif + } + + if (!baseonly) + num_optional_paths = ARRAY_SIZE(opt_suffixes); + + total_paths = num_required_paths + num_optional_paths; + + /* Make sure total input files do not exceed the 256 indices supported + * by uint8_t inputno */ + if (total_paths > UINT8_MAX + 1) { + errno = EINVAL; + return -1; + } + + /* Allocate the paths. */ + rec->spec_files = calloc(total_paths, sizeof(*rec->spec_files)); + if (rec->spec_files == NULL) + goto finish; + rec->spec_files_len = total_paths; + + /* Copy all the paths given. */ + if (path_provided) { + for (i = 0, j = 0; i < n; i++) { + if (opts[i].type == SELABEL_OPT_PATH && opts[i].value) { + rec->spec_files[j] = strdup(opts[i].value); + if (rec->spec_files[j] == NULL) + goto finish; + j++; + } + } + } + #if !defined(BUILD_HOST) && !defined(ANDROID) char subs_file[PATH_MAX + 1]; /* Process local and distribution substitution files */ - if (!path) { + if (!path_provided) { + rec->spec_files[0] = strdup(selinux_file_context_path()); + if (rec->spec_files[0] == NULL) + goto finish; status = selabel_subs_init( selinux_file_context_subs_dist_path(), rec->digest, &data->dist_subs, &data->dist_subs_num, @@ -1526,67 +1590,61 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, &data->subs_num, &data->subs_alloc); if (status) goto finish; - path = selinux_file_context_path(); } else { - snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path); + snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", + rec->spec_files[0]); status = selabel_subs_init(subs_file, rec->digest, &data->dist_subs, &data->dist_subs_num, &data->dist_subs_alloc); if (status) goto finish; - snprintf(subs_file, sizeof(subs_file), "%s.subs", path); + snprintf(subs_file, sizeof(subs_file), "%s.subs", + rec->spec_files[0]); status = selabel_subs_init(subs_file, rec->digest, &data->subs, &data->subs_num, &data->subs_alloc); if (status) goto finish; } - #endif - if (!path) { - errno = EINVAL; - goto finish; + for (i = 0; i < num_optional_paths; i++) { + if (asprintf(&rec->spec_files[num_required_paths + i], "%s.%s", + rec->spec_files[0], opt_suffixes[i]) < 0) { + rec->spec_files[num_required_paths + i] = NULL; + goto finish; + } } - rec->spec_files = calloc(1, sizeof(*rec->spec_files)); - if (!rec->spec_files) - goto finish; - rec->spec_files[0] = strdup(path); - if (!rec->spec_files[0]) - goto finish; - rec->spec_files_len = 1; - /* - * The do detailed validation of the input and fill the spec array + * Process each main input file. */ - status = process_file(path, NULL, rec, prefix, rec->digest, 0); - if (status) - goto finish; - - if (rec->validating) { - sort_specs(data); - - status = nodups_spec_node(data->root, path); + for (i = 0; i < num_required_paths; i++) { + status = process_file(rec->spec_files[i], NULL, rec, prefix, + rec->digest, i); if (status) goto finish; } - if (!baseonly) { - status = process_file(path, "homedirs", rec, prefix, - rec->digest, 1); + /* + * Process each optional input file. + */ + for (i = 0; i < num_optional_paths; i++) { + status = process_file(rec->spec_files[num_required_paths + i], + NULL, rec, prefix, rec->digest, + num_required_paths + i); if (status && errno != ENOENT) goto finish; + } - status = process_file(path, "local", rec, prefix, rec->digest, - 2); - if (status && errno != ENOENT) + sort_specs(data); + + if (rec->validating) { + status = nodups_spec_node(rec, data->root); + if (status) goto finish; } - if (!rec->validating || !baseonly) - sort_specs(data); - digest_gen_hash(rec->digest); status = 0; diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h index d54053df..4279637e 100644 --- a/libselinux/src/label_internal.h +++ b/libselinux/src/label_internal.h @@ -96,8 +96,7 @@ struct selabel_handle { void *data; /* - * The spec files used. Note for file contexts the local and/or - * homedirs could also have been used to resolve a context. + * The specification files used. */ size_t spec_files_len; char **spec_files; -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v3 2/2] libselinux: support multiple context files for the file backend 2026-07-27 1:39 ` [PATCH v3 2/2] libselinux: support multiple context files for the file backend Thiébaud Weksteen @ 2026-07-27 13:30 ` Stephen Smalley 0 siblings, 0 replies; 4+ messages in thread From: Stephen Smalley @ 2026-07-27 13:30 UTC (permalink / raw) To: Thiébaud Weksteen Cc: selinux, James Carter, Christian Göttsche, Ondrej Mosnacek On Sun, Jul 26, 2026 at 9:39 PM Thiébaud Weksteen <tweek@google.com> wrote: > > Update the file labeling backend to support specifying multiple > SELABEL_OPT_PATH options in selabel_open(). > > All provided paths are stored in rec->spec_files and processed during > initialization. Derived files, such as substitutions (.subs, .subs_dist) > and auxiliary contexts (.homedirs, .local), continue to be based on the > first path (or default selinux_file_context_path()) when enabled. > > Duplicate checking is only performed within each context file. The current > behavior expects the ability for a later file to overwrite a previous > declaration (e.g., .local). > > Signed-off-by: Thiébaud Weksteen <tweek@google.com> Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com> > --- > Changes since v2: > - Add optional paths (homedirs, local) to spec_files. Since these might > be referenced, let's keep track of them as any other spec files. > spec_files now contains the required files followed by the optional > files. > - Strictly validate that, at most, 255 options are provided to init(). > - Remove the intermediate `path` variable. > - Note: the 2rd argument of process_file is now always NULL. This can be > cleaned in a follow up commit. > > Changes since v1: > - Restore nodups_spec_node, ignore duplicates across files. > - Change num_paths type to uint8_t and check for overflow. > - Check for NULL before strdup any path. > - Use num_paths and num_paths+1 for homedirs and local. > > libselinux/src/label_file.c | 154 ++++++++++++++++++++++---------- > libselinux/src/label_internal.h | 3 +- > 2 files changed, 107 insertions(+), 50 deletions(-) > > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c > index d91e1462..c955216b 100644 > --- a/libselinux/src/label_file.c > +++ b/libselinux/src/label_file.c > @@ -106,7 +106,8 @@ void sort_spec_node(struct spec_node *node, struct spec_node *parent) > /* > * Warn about duplicate specifications. > */ > -static int nodups_spec_node(const struct spec_node *node, const char *path) > +static int nodups_spec_node(const struct selabel_handle *rec, > + const struct spec_node *node) > { > int rc = 0; > > @@ -126,13 +127,16 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > node1->file_kind != node2->file_kind) > continue; > > + if (node1->inputno != node2->inputno) > + continue; > + > rc = -1; > errno = EINVAL; > if (strcmp(node1->lr.ctx_raw, node2->lr.ctx_raw) != 0) { > COMPAT_LOG( > SELINUX_ERROR, > "%s: Multiple different specifications for %s %s (%s and %s).\n", > - path, > + rec->spec_files[node1->inputno], > file_kind_to_string(node1->file_kind), > node1->literal_match, node1->lr.ctx_raw, > node2->lr.ctx_raw); > @@ -140,7 +144,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > COMPAT_LOG( > SELINUX_ERROR, > "%s: Multiple same specifications for %s %s.\n", > - path, > + rec->spec_files[node1->inputno], > file_kind_to_string(node1->file_kind), > node1->literal_match); > } > @@ -168,6 +172,9 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > node1->file_kind != node2->file_kind) > continue; > > + if (node1->inputno != node2->inputno) > + continue; > + > rc = -1; > errno = EINVAL; > if (strcmp(node1->lr.ctx_raw, > @@ -175,7 +182,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > COMPAT_LOG( > SELINUX_ERROR, > "%s: Multiple different specifications for %s %s (%s and %s).\n", > - path, > + rec->spec_files[node1->inputno], > file_kind_to_string( > node1->file_kind), > node1->regex_str, > @@ -185,7 +192,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > COMPAT_LOG( > SELINUX_ERROR, > "%s: Multiple same specifications for %s %s.\n", > - path, > + rec->spec_files[node1->inputno], > file_kind_to_string( > node1->file_kind), > node1->regex_str); > @@ -197,7 +204,7 @@ static int nodups_spec_node(const struct spec_node *node, const char *path) > for (uint32_t i = 0; i < node->children_num; i++) { > int rc2; > > - rc2 = nodups_spec_node(&node->children[i], path); > + rc2 = nodups_spec_node(rec, &node->children[i]); > if (rc2) > rc = rc2; > } > @@ -1480,26 +1487,37 @@ static char *selabel_sub_key(const struct saved_data *data, const char *key, > > static void closef(struct selabel_handle *rec); > > +static const char *const opt_suffixes[] = { "homedirs", "local" }; > + > static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > unsigned n) > { > struct saved_data *data = rec->data; > - const char *path = NULL; > + uint8_t num_required_paths = 0, num_optional_paths = 0, i, j; > + size_t total_paths; > const char *prefix = NULL; > - int status = -1, baseonly = 0; > + int status = -1; > + bool baseonly = false, path_provided = false; > + > + if (n > UINT8_MAX) { > + errno = EINVAL; > + return -1; > + } > > /* Process arguments */ > - while (n) { > - n--; > - switch (opts[n].type) { > + for (i = 0; i < n; i++) { > + switch (opts[i].type) { > case SELABEL_OPT_PATH: > - path = opts[n].value; > + if (opts[i].value) { > + num_required_paths++; > + path_provided = true; > + } > break; > case SELABEL_OPT_SUBSET: > - prefix = opts[n].value; > + prefix = opts[i].value; > break; > case SELABEL_OPT_BASEONLY: > - baseonly = !!opts[n].value; > + baseonly = !!opts[i].value; > break; > case SELABEL_OPT_UNUSED: > case SELABEL_OPT_VALIDATE: > @@ -1511,10 +1529,56 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > } > } > > + /* If no paths were provided, we will use the default path or fail, > + * depending on the target. */ > + if (!path_provided) { > +#if !defined(BUILD_HOST) && !defined(ANDROID) > + num_required_paths = 1; > +#else > + selinux_log(SELINUX_ERROR, > + "No path given to file labeling backend\n"); > + errno = EINVAL; > + return -1; > +#endif > + } > + > + if (!baseonly) > + num_optional_paths = ARRAY_SIZE(opt_suffixes); > + > + total_paths = num_required_paths + num_optional_paths; > + > + /* Make sure total input files do not exceed the 256 indices supported > + * by uint8_t inputno */ > + if (total_paths > UINT8_MAX + 1) { > + errno = EINVAL; > + return -1; > + } > + > + /* Allocate the paths. */ > + rec->spec_files = calloc(total_paths, sizeof(*rec->spec_files)); > + if (rec->spec_files == NULL) > + goto finish; > + rec->spec_files_len = total_paths; > + > + /* Copy all the paths given. */ > + if (path_provided) { > + for (i = 0, j = 0; i < n; i++) { > + if (opts[i].type == SELABEL_OPT_PATH && opts[i].value) { > + rec->spec_files[j] = strdup(opts[i].value); > + if (rec->spec_files[j] == NULL) > + goto finish; > + j++; > + } > + } > + } > + > #if !defined(BUILD_HOST) && !defined(ANDROID) > char subs_file[PATH_MAX + 1]; > /* Process local and distribution substitution files */ > - if (!path) { > + if (!path_provided) { > + rec->spec_files[0] = strdup(selinux_file_context_path()); > + if (rec->spec_files[0] == NULL) > + goto finish; > status = selabel_subs_init( > selinux_file_context_subs_dist_path(), rec->digest, > &data->dist_subs, &data->dist_subs_num, > @@ -1526,67 +1590,61 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > &data->subs_num, &data->subs_alloc); > if (status) > goto finish; > - path = selinux_file_context_path(); > } else { > - snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path); > + snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", > + rec->spec_files[0]); > status = selabel_subs_init(subs_file, rec->digest, > &data->dist_subs, > &data->dist_subs_num, > &data->dist_subs_alloc); > if (status) > goto finish; > - snprintf(subs_file, sizeof(subs_file), "%s.subs", path); > + snprintf(subs_file, sizeof(subs_file), "%s.subs", > + rec->spec_files[0]); > status = selabel_subs_init(subs_file, rec->digest, &data->subs, > &data->subs_num, &data->subs_alloc); > if (status) > goto finish; > } > - > #endif > > - if (!path) { > - errno = EINVAL; > - goto finish; > + for (i = 0; i < num_optional_paths; i++) { > + if (asprintf(&rec->spec_files[num_required_paths + i], "%s.%s", > + rec->spec_files[0], opt_suffixes[i]) < 0) { > + rec->spec_files[num_required_paths + i] = NULL; > + goto finish; > + } > } > > - rec->spec_files = calloc(1, sizeof(*rec->spec_files)); > - if (!rec->spec_files) > - goto finish; > - rec->spec_files[0] = strdup(path); > - if (!rec->spec_files[0]) > - goto finish; > - rec->spec_files_len = 1; > - > /* > - * The do detailed validation of the input and fill the spec array > + * Process each main input file. > */ > - status = process_file(path, NULL, rec, prefix, rec->digest, 0); > - if (status) > - goto finish; > - > - if (rec->validating) { > - sort_specs(data); > - > - status = nodups_spec_node(data->root, path); > + for (i = 0; i < num_required_paths; i++) { > + status = process_file(rec->spec_files[i], NULL, rec, prefix, > + rec->digest, i); > if (status) > goto finish; > } > > - if (!baseonly) { > - status = process_file(path, "homedirs", rec, prefix, > - rec->digest, 1); > + /* > + * Process each optional input file. > + */ > + for (i = 0; i < num_optional_paths; i++) { > + status = process_file(rec->spec_files[num_required_paths + i], > + NULL, rec, prefix, rec->digest, > + num_required_paths + i); > if (status && errno != ENOENT) > goto finish; > + } > > - status = process_file(path, "local", rec, prefix, rec->digest, > - 2); > - if (status && errno != ENOENT) > + sort_specs(data); > + > + if (rec->validating) { > + status = nodups_spec_node(rec, data->root); > + if (status) > goto finish; > } > > - if (!rec->validating || !baseonly) > - sort_specs(data); > - > digest_gen_hash(rec->digest); > > status = 0; > diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h > index d54053df..4279637e 100644 > --- a/libselinux/src/label_internal.h > +++ b/libselinux/src/label_internal.h > @@ -96,8 +96,7 @@ struct selabel_handle { > void *data; > > /* > - * The spec files used. Note for file contexts the local and/or > - * homedirs could also have been used to resolve a context. > + * The specification files used. > */ > size_t spec_files_len; > char **spec_files; > -- > 2.55.0.229.g6434b31f56-goog > ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3 1/2] libselinux: support multiple spec_files 2026-07-27 1:39 [PATCH v3 1/2] libselinux: support multiple spec_files Thiébaud Weksteen 2026-07-27 1:39 ` [PATCH v3 2/2] libselinux: support multiple context files for the file backend Thiébaud Weksteen @ 2026-07-27 13:30 ` Stephen Smalley 1 sibling, 0 replies; 4+ messages in thread From: Stephen Smalley @ 2026-07-27 13:30 UTC (permalink / raw) To: Thiébaud Weksteen Cc: selinux, James Carter, Christian Göttsche, Ondrej Mosnacek On Sun, Jul 26, 2026 at 9:39 PM Thiébaud Weksteen <tweek@google.com> wrote: > > Update the existing backends to potentially allocate multiple > spec_files. This commit simply adds a level of indirection but does not > change any current behaviour. It will facilitate the gradual migration > of backends to multiple context files. > > In selabel_fini(), compat_validate() is updated to use the first > spec_file only, for legacy validation error reporting. selabel_open() is > already performing context validation if requested against individual > file paths (see insert_spec). > > Signed-off-by: Thiébaud Weksteen <tweek@google.com> You can keep Acked-by/Reviewed-by/Tested-by when the patch is unchanged. Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com> > --- > No changes since v2 > > libselinux/src/label.c | 10 ++++++++-- > libselinux/src/label_db.c | 11 +++++++++-- > libselinux/src/label_file.c | 8 ++++++-- > libselinux/src/label_internal.h | 5 +++-- > libselinux/src/label_media.c | 8 +++++++- > libselinux/src/label_x.c | 8 +++++++- > 6 files changed, 40 insertions(+), 10 deletions(-) > > diff --git a/libselinux/src/label.c b/libselinux/src/label.c > index 991b9769..4a97d3bc 100644 > --- a/libselinux/src/label.c > +++ b/libselinux/src/label.c > @@ -162,7 +162,7 @@ static int selabel_fini(const struct selabel_handle *rec, > char *ctx_trans; > int rc; > > - if (compat_validate(rec, lr, rec->spec_file, lr->lineno)) > + if (compat_validate(rec, lr, rec->spec_files[0], lr->lineno)) > return -1; > > if (!translating) > @@ -398,11 +398,17 @@ int selabel_digest(struct selabel_handle *rec, unsigned char **digest, > > void selabel_close(struct selabel_handle *rec) > { > + size_t i; > + > if (rec->digest) > selabel_digest_fini(rec->digest); > if (rec->func_close) > rec->func_close(rec); > - free(rec->spec_file); > + if (rec->spec_files) { > + for (i = 0; i < rec->spec_files_len; i++) > + free(rec->spec_files[i]); > + free(rec->spec_files); > + } > free(rec); > } > > diff --git a/libselinux/src/label_db.c b/libselinux/src/label_db.c > index bafa9328..53731470 100644 > --- a/libselinux/src/label_db.c > +++ b/libselinux/src/label_db.c > @@ -302,12 +302,19 @@ static catalog_t *db_init(const struct selinux_opt *opts, unsigned nopts, > errno = EINVAL; > return NULL; > } > - rec->spec_file = strdup(path); > - if (!rec->spec_file) { > + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); > + if (!rec->spec_files) { > free(catalog); > fclose(filp); > return NULL; > } > + rec->spec_files[0] = strdup(path); > + if (!rec->spec_files[0]) { > + free(catalog); > + fclose(filp); > + return NULL; > + } > + rec->spec_files_len = 1; > > /* > * Parse for each lines > diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c > index 0c0499eb..d91e1462 100644 > --- a/libselinux/src/label_file.c > +++ b/libselinux/src/label_file.c > @@ -1549,9 +1549,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > goto finish; > } > > - rec->spec_file = strdup(path); > - if (!rec->spec_file) > + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); > + if (!rec->spec_files) > goto finish; > + rec->spec_files[0] = strdup(path); > + if (!rec->spec_files[0]) > + goto finish; > + rec->spec_files_len = 1; > > /* > * The do detailed validation of the input and fill the spec array > diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h > index 4ff39d96..d54053df 100644 > --- a/libselinux/src/label_internal.h > +++ b/libselinux/src/label_internal.h > @@ -96,10 +96,11 @@ struct selabel_handle { > void *data; > > /* > - * The main spec file used. Note for file contexts the local and/or > + * The spec files used. Note for file contexts the local and/or > * homedirs could also have been used to resolve a context. > */ > - char *spec_file; > + size_t spec_files_len; > + char **spec_files; > > /* ptr to SHA1 hash information if SELABEL_OPT_DIGEST set */ > struct selabel_digest *digest; > diff --git a/libselinux/src/label_media.c b/libselinux/src/label_media.c > index 957fcfd3..f315abc3 100644 > --- a/libselinux/src/label_media.c > +++ b/libselinux/src/label_media.c > @@ -109,7 +109,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > errno = EINVAL; > goto finish; > } > - rec->spec_file = strdup(path); > + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); > + if (!rec->spec_files) > + goto finish; > + rec->spec_files[0] = strdup(path); > + if (!rec->spec_files[0]) > + goto finish; > + rec->spec_files_len = 1; > > /* > * Perform two passes over the specification file. > diff --git a/libselinux/src/label_x.c b/libselinux/src/label_x.c > index 0c525bfc..b528a019 100644 > --- a/libselinux/src/label_x.c > +++ b/libselinux/src/label_x.c > @@ -139,7 +139,13 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts, > errno = EINVAL; > goto finish; > } > - rec->spec_file = strdup(path); > + rec->spec_files = calloc(1, sizeof(*rec->spec_files)); > + if (!rec->spec_files) > + goto finish; > + rec->spec_files[0] = strdup(path); > + if (!rec->spec_files[0]) > + goto finish; > + rec->spec_files_len = 1; > > /* > * Perform two passes over the specification file. > -- > 2.55.0.229.g6434b31f56-goog > ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-27 13:30 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-27 1:39 [PATCH v3 1/2] libselinux: support multiple spec_files Thiébaud Weksteen 2026-07-27 1:39 ` [PATCH v3 2/2] libselinux: support multiple context files for the file backend Thiébaud Weksteen 2026-07-27 13:30 ` Stephen Smalley 2026-07-27 13:30 ` [PATCH v3 1/2] libselinux: support multiple spec_files Stephen Smalley
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.