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) > > > + 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 > >