On Wed, Nov 1, 2023 at 12:47 PM Christian Göttsche <cgzones@xxxxxxxxxxxxxx> wrote: > > On Thu, 5 Oct 2023 at 17:29, James Carter <jwcart2@xxxxxxxxx> wrote: > > > > On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche > > <cgzones@xxxxxxxxxxxxxx> wrote: > > > > > > Add a utility around selabel_cmp(3). > > > > > > Can be used by users to compare a pre-compiled fcontext file to an > > > original text-based file context definition file. > > > > > > Can be used for development to verify compilation and parsing of the > > > pre-compiled fcontext format works correctly. > > > > > > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> > > > --- > > > libselinux/utils/.gitignore | 1 + > > > libselinux/utils/selabel_compare.c | 119 +++++++++++++++++++++++++++++ > > > 2 files changed, 120 insertions(+) > > > create mode 100644 libselinux/utils/selabel_compare.c > > > > > > diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore > > > index a92e1e94..4e2dfba8 100644 > > > --- a/libselinux/utils/.gitignore > > > +++ b/libselinux/utils/.gitignore > > > @@ -16,6 +16,7 @@ getseuser > > > matchpathcon > > > policyvers > > > sefcontext_compile > > > +selabel_compare > > > selabel_digest > > > selabel_get_digests_all_partial_matches > > > selabel_lookup > > > diff --git a/libselinux/utils/selabel_compare.c b/libselinux/utils/selabel_compare.c > > > new file mode 100644 > > > index 00000000..f4325f7e > > > --- /dev/null > > > +++ b/libselinux/utils/selabel_compare.c > > > @@ -0,0 +1,119 @@ > > > +#include <getopt.h> > > > +#include <stdio.h> > > > +#include <stdlib.h> > > > +#include <string.h> > > > + > > > +#include <selinux/label.h> > > > + > > > + > > > +static void usage(const char *progname) > > > +{ > > > + fprintf(stderr, > > > + "usage: %s [-b backend] [-v] file1 file2\n\n" > > > + "Where:\n\t" > > > + "-b The backend - \"file\", \"media\", \"x\", \"db\" or \"prop\" (defaults to \"file\")\n\t" > > > + "-v Validate entries against loaded policy.\n\t" > > > + "file1/file2 Files containing the specs.\n", > > > + progname); > > > +} > > > + > > > +int main(int argc, char *argv[]) > > > +{ > > > + unsigned int backend = SELABEL_CTX_FILE; > > > + int opt; > > > + const char *validate = NULL, *file1 = NULL, *file2 = NULL; > > > + > > > + if (argc < 3) { > > > + usage(argv[0]); > > > + return EXIT_FAILURE; > > > + } > > > + > > > + while ((opt = getopt(argc, argv, "b:v")) > 0) { > > > + switch (opt) { > > > + case 'b': > > > + if (!strcasecmp(optarg, "file")) { > > > + backend = SELABEL_CTX_FILE; > > > + } else if (!strcmp(optarg, "media")) { > > > + backend = SELABEL_CTX_MEDIA; > > > + } else if (!strcmp(optarg, "x")) { > > > + backend = SELABEL_CTX_X; > > > + } else if (!strcmp(optarg, "db")) { > > > + backend = SELABEL_CTX_DB; > > > + } else if (!strcmp(optarg, "prop")) { > > > + backend = SELABEL_CTX_ANDROID_PROP; > > > + } else if (!strcmp(optarg, "service")) { > > > + backend = SELABEL_CTX_ANDROID_SERVICE; > > > + } else { > > > + fprintf(stderr, "Unknown backend: %s\n", optarg); > > > + usage(argv[0]); > > > + return EXIT_FAILURE; > > > + } > > > + break; > > > + case 'v': > > > + validate = (char *)1; > > > + break; > > > + default: > > > + usage(argv[0]); > > > + return EXIT_FAILURE; > > > + } > > > + } > > > + > > > + if (argc != optind + 2) { > > > + usage(argv[0]); > > > + return EXIT_FAILURE; > > > + } > > > + > > > + file1 = argv[optind++]; > > > + file2 = argv[optind]; > > > + > > > + { > > > > To me, having a block like this means that the stuff below should be > > in a helper function. > > > > Everything else looks good. > > Jim > > > > I didn't want to split the function to complicate the reasoning by > splitting the control flow, introducing 4 function parameters, and > adding return value handling. > Do you still prefer to split? > (I'll send a v2 regardless for a typo below) > Yes, I would. Something like this: static enum selabel_cmp_result compare(unsigned int backend, const char *file1, const char *file2, const char *validate) { struct selabel_handle *hnd1, *hnd2; const struct selinux_opt selabel_option1[] = { { SELABEL_OPT_PATH, file1 }, { SELABEL_OPT_VALIDATE, validate } }; const struct selinux_opt selabel_option2[] = { { SELABEL_OPT_PATH, file2 }, { SELABEL_OPT_VALIDATE, validate } }; enum selabel_cmp_result result; hnd1 = selabel_open(backend, selabel_option1, 2); if (!hnd1) { fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file1); return EXIT_FAILURE; } hnd2 = selabel_open(backend, selabel_option2, 2); if (!hnd2) { fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file2); selabel_close(hnd1); return EXIT_FAILURE; } result = selabel_cmp(hnd1, hnd2); selabel_close(hnd2); selabel_close(hnd1); return result; } ... int main(int argc, char *argv[]) { ... result = compare(backend, file1, file2, validate); switch (result) { case SELABEL_SUBSET: printf("spec %s is a subset of spec %s\n", file1, file2); break; case SELABEL_EQUAL: printf("spec %s is equal to spec %s\n", file1, file2); break; case SELABEL_SUPERSET: printf("spec %s is a superset of spec %s\n", file1, file2); break; case SELABEL_INCOMPARABLE: printf("spec %s is uncompareable to spec %s\n", file1, file2); break; default: fprintf(stderr, "ERROR: selabel_cmp - Unexpected result %d\n", result); return EXIT_FAILURE; } return EXIT_SUCCESS; } You can probably think of a better name for the function. Thanks, Jim > > > > > + struct selabel_handle *hnd1, *hnd2; > > > + const struct selinux_opt selabel_option1[] = { > > > + { SELABEL_OPT_PATH, file1 }, > > > + { SELABEL_OPT_VALIDATE, validate } > > > + }; > > > + const struct selinux_opt selabel_option2[] = { > > > + { SELABEL_OPT_PATH, file2 }, > > > + { SELABEL_OPT_VALIDATE, validate } > > > + }; > > > + enum selabel_cmp_result result; > > > + > > > + hnd1 = selabel_open(backend, selabel_option1, 2); > > > + if (!hnd1) { > > > + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file1); > > > + return EXIT_FAILURE; > > > + } > > > + > > > + hnd2 = selabel_open(backend, selabel_option2, 2); > > > + if (!hnd2) { > > > + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file2); > > > + selabel_close(hnd1); > > > + return EXIT_FAILURE; > > > + } > > > + > > > + result = selabel_cmp(hnd1, hnd2); > > > + > > > + selabel_close(hnd2); > > > + selabel_close(hnd1); > > > + > > > + switch (result) { > > > + case SELABEL_SUBSET: > > > + printf("spec %s is a subset of spec %s\n", file1, file2); > > > + break; > > > + case SELABEL_EQUAL: > > > + printf("spec %s is equal to spec %s\n", file1, file2); > > > + break; > > > + case SELABEL_SUPERSET: > > > + printf("spec %s is a superset of spec %s\n", file1, file2); > > > + break; > > > + case SELABEL_INCOMPARABLE: > > > + printf("spec %s is uncompareable to spec %s\n", file1, file2); > > > + break; > > > + default: > > > + fprintf(stderr, "ERROR: selabel_cmp - Unexpected result %d\n", result); > > > + return EXIT_FAILURE; > > > + } > > > + > > > + return EXIT_SUCCESS; > > > + } > > > +} > > > -- > > > 2.40.1 > > >