+++ /dev/null
-/*
- * setfiles
- *
- * AUTHOR: Stephen Smalley <sds@epoch.ncsc.mil>
- * This program was derived in part from the setfiles.pl script
- * developed by Secure Computing Corporation.
- *
- * PURPOSE:
- * This program reads a set of file security context specifications
- * based on pathname regular expressions and labels files
- * accordingly, traversing a set of file systems specified by
- * the user. The program does not cross file system boundaries.
- *
- * USAGE:
- * setfiles [-dnpqsvW] spec_file pathname...
- *
- * -d Show what specification matched each file.
- * -n Do not change any file labels.
- * -q Be quiet (suppress non-error output).
- * -r Use an alternate root path
- * -s Use stdin for a list of files instead of searching a partition.
- * -v Show changes in file labels.
- * -W Warn about entries that have no matching file.
- *
- * spec_file The specification file.
- * pathname... The file systems to label (omit if using -s).
- *
- * EXAMPLE USAGE:
- * ./setfiles -v file_contexts `mount | awk '/ext3/{print $3}'`
- *
- * SPECIFICATION FILE:
- * Each specification has the form:
- * regexp [ -type ] ( context | <<none>> )
- *
- * By default, the regexp is an anchored match on both ends (i.e. a
- * caret (^) is prepended and a dollar sign ($) is appended automatically).
- * This default may be overridden by using .* at the beginning and/or
- * end of the regular expression.
- *
- * The optional type field specifies the file type as shown in the mode
- * field by ls, e.g. use -d to match only directories or -- to match only
- * regular files.
- *
- * The value of <<none> may be used to indicate that matching files
- * should not be relabeled.
- *
- * The last matching specification is used.
- *
- * If there are multiple hard links to a file that match
- * different specifications and those specifications indicate
- * different security contexts, then a warning is displayed
- * but the file is still labeled based on the last matching
- * specification other than <<none>>.
- */
-
-#include "system.h"
-const char *__progname;
-
-#include <regex.h>
-#include <sys/vfs.h>
-#define __USE_XOPEN_EXTENDED 1 /* nftw */
-#include <ftw.h>
-
-#include <selinux/selinux.h>
-#include <popt.h>
-
-#include "debug.h"
-
-static int add_assoc = 1;
-
-/*
- * Command-line options.
- */
-static int debug = 0;
-static int change = 1;
-static int quiet = 0;
-#define QPRINTF(args...) do { if (!quiet) printf(args); } while (0)
-static int use_stdin = 0;
-static int verbose = 0;
-static int warn_no_match = 0;
-static char *rootpath = NULL;
-static int rootpathlen = 0;
-
-/*
- * A file security context specification.
- */
-typedef struct spec {
-/*@only@*/
- char *pattern; /* regular expession string for diagnostic messages */
-/*@only@*/
- char *type_str; /* type string for diagnostic messages */
-/*@only@*/
- char *context; /* context string */
-/*@only@*/
- regex_t * preg; /* compiled regular expression */
- mode_t mode; /* mode format value */
- int matches; /* number of matching pathnames */
- int hasMetaChars; /* indicates whether the RE has
- any meta characters.
- 0 = no meta chars
- 1 = has one or more meta chars */
- int stem_id; /* indicates which of the stem-compression
- items it matches */
-} * spec_t;
-
-typedef struct stem {
- char *buf;
- int len;
-} * stem_t;
-
-stem_t stem_arr = NULL;
-int num_stems = 0;
-int alloc_stems = 0;
-
-const char * const regex_chars = ".^$?*+|[({";
-
-/* Return the length of the text that can be considered the stem, returns 0
- * if there is no identifiable stem */
-static int get_stem_from_spec(const char * const buf)
-{
- const char *tmp = strchr(buf + 1, '/');
- const char *ind;
-
- if (!tmp)
- return 0;
-
- for (ind = buf + 1; ind < tmp; ind++) {
- if (strchr(regex_chars, (int)*ind))
- return 0;
- }
- return tmp - buf;
-}
-
-/* return the length of the text that is the stem of a file name */
-static int get_stem_from_file_name(const char * const buf)
-{
- const char *tmp = strchr(buf + 1, '/');
-
- if (!tmp)
- return 0;
- return tmp - buf;
-}
-
-/* find the stem of a file spec, returns the index into stem_arr for a new
- * or existing stem, (or -1 if there is no possible stem - IE for a file in
- * the root directory or a regex that is too complex for us). Makes buf
- * point to the text AFTER the stem. */
-static int find_stem_from_spec(const char **buf)
-{
- int stem_len = get_stem_from_spec(*buf);
- int i;
-
- if (!stem_len)
- return -1;
- for (i = 0; i < num_stems; i++) {
- if (stem_len != stem_arr[i].len)
- continue;
- if (strncmp(*buf, stem_arr[i].buf, stem_len))
- continue;
- *buf += stem_len;
- return i;
- }
-
- if (num_stems == alloc_stems) {
- alloc_stems = alloc_stems * 2 + 16;
- stem_arr = xrealloc(stem_arr, sizeof(*stem_arr) * alloc_stems);
- }
- stem_arr[num_stems].len = stem_len;
- stem_arr[num_stems].buf = xmalloc(stem_len + 1);
- memcpy(stem_arr[num_stems].buf, *buf, stem_len);
- stem_arr[num_stems].buf[stem_len] = '\0';
- num_stems++;
- *buf += stem_len;
- return num_stems - 1;
-}
-
-/* find the stem of a file name, returns the index into stem_arr (or -1 if
- * there is no match - IE for a file in the root directory or a regex that is
- * too complex for us). Makes buf point to the text AFTER the stem. */
-static int find_stem_from_file(const char **buf)
-{
- int stem_len = get_stem_from_file_name(*buf);
- int i;
-
- if (stem_len)
- for (i = 0; i < num_stems; i++) {
- if (stem_len != stem_arr[i].len)
- continue;
- if (strncmp(*buf, stem_arr[i].buf, stem_len))
- continue;
- *buf += stem_len;
- return i;
- }
- return -1;
-}
-
-/*
- * The array of specifications, initially in the
- * same order as in the specification file.
- * Sorting occurs based on hasMetaChars
- */
-static spec_t spec_arr;
-static int nspec;
-
-/*
- * An association between an inode and a
- * specification.
- */
-typedef struct file_spec * file_spec_t;
-struct file_spec {
- ino_t ino; /* inode number */
- int specind; /* index of specification in spec */
- char *file; /* full pathname for diagnostic messages about conflicts */
- file_spec_t next; /* next association in hash bucket chain */
-};
-
-/*
- * The hash table of associations, hashed by inode number.
- * Chaining is used for collisions, with elements ordered
- * by inode number in each bucket. Each hash bucket has a dummy
- * header.
- */
-#define HASH_BITS 16
-#define HASH_BUCKETS (1 << HASH_BITS)
-#define HASH_MASK (HASH_BUCKETS-1)
-static struct file_spec fl_head[HASH_BUCKETS];
-
-/*
- * Try to add an association between an inode and
- * a specification. If there is already an association
- * for the inode and it conflicts with this specification,
- * then use the specification that occurs later in the
- * specification array.
- */
-static file_spec_t file_spec_add(ino_t ino, int specind, const char *file)
-{
- file_spec_t prevfl;
- file_spec_t fl;
- int h, no_conflict, ret;
- struct stat sb;
-
- h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
- for (prevfl = &fl_head[h], fl = fl_head[h].next;
- fl != NULL;
- prevfl = fl, fl = fl->next)
- {
- if (ino == fl->ino) {
- ret = lstat(fl->file, &sb);
- if (ret < 0 || sb.st_ino != ino) {
- fl->specind = specind;
- free(fl->file);
- fl->file = xstrdup(file);
- return fl;
- }
-
- no_conflict = (strcmp(spec_arr[fl->specind].context,spec_arr[specind].context) == 0);
- if (no_conflict)
- return fl;
-
- fprintf(stderr,
- "%s: conflicting specifications for %s and %s, using %s.\n",
- __progname, file, fl->file,
- ((specind > fl->specind) ? spec_arr[specind].
- context : spec_arr[fl->specind].context));
- fl->specind =
- (specind > fl->specind) ? specind : fl->specind;
- free(fl->file);
- fl->file = xstrdup(file);
- return fl;
- }
-
- if (ino > fl->ino)
- break;
- }
-
- fl = xmalloc(sizeof(*fl));
- fl->ino = ino;
- fl->specind = specind;
- fl->file = xstrdup(file);
- fl->next = prevfl->next;
- prevfl->next = fl;
- return fl;
-}
-
-/*
- * Evaluate the association hash table distribution.
- */
-static void file_spec_eval(void)
-{
- file_spec_t fl;
- int h, used, nel, len, longest;
-
- used = 0;
- longest = 0;
- nel = 0;
- for (h = 0; h < HASH_BUCKETS; h++) {
- len = 0;
- for (fl = fl_head[h].next; fl; fl = fl->next) {
- len++;
- }
- if (len)
- used++;
- if (len > longest)
- longest = len;
- nel += len;
- }
-
- QPRINTF ("%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
- __progname, nel, used, HASH_BUCKETS, longest);
-}
-
-
-/*
- * Destroy the association hash table.
- */
-static void file_spec_destroy(void)
-{
- file_spec_t fl;
- file_spec_t tmp;
- int h;
-
- for (h = 0; h < HASH_BUCKETS; h++) {
- fl = fl_head[h].next;
- while (fl) {
- tmp = fl;
- fl = fl->next;
- free(tmp->file);
- free(tmp);
- }
- fl_head[h].next = NULL;
- }
-}
-
-
-static int match(const char *name, struct stat *sb)
-{
- static char errbuf[255 + 1];
- const char *fullname = name;
- const char *buf = name;
- int i, ret, file_stem;
-
- /* fullname will be the real file that gets labeled
- * name will be what is matched in the policy */
- if (rootpath != NULL) {
- if (strncmp(rootpath, name, rootpathlen) != 0) {
- fprintf(stderr, "%s: %s is not located in %s\n",
- __progname, name, rootpath);
- return -1;
- }
- name += rootpathlen;
- buf += rootpathlen;
- }
-
- ret = lstat(fullname, sb);
- if (ret) {
- fprintf(stderr, "%s: unable to stat file %s\n", __progname, fullname);
- return -1;
- }
-
- file_stem = find_stem_from_file(&buf);
-
- /*
- * Check for matching specifications in reverse order, so that
- * the last matching specification is used.
- */
- for (i = nspec - 1; i >= 0; i--) {
- spec_t sp;
-
- sp = spec_arr + i;
-
- /* if the spec in question matches no stem or has the same
- * stem as the file AND if the spec in question has no mode
- * specified or if the mode matches the file mode then we do
- * a regex check */
- if (sp->stem_id != -1 && sp->stem_id != file_stem)
- continue;
- if (sp->mode && (sb->st_mode & S_IFMT) != sp->mode)
- continue;
-
- if (sp->stem_id == -1)
- ret = regexec(sp->preg, name, 0, NULL, 0);
- else
- ret = regexec(sp->preg, buf, 0, NULL, 0);
- if (ret == 0)
- break;
-
- if (ret == REG_NOMATCH)
- continue;
-
- /* else it's an error */
- regerror(ret, sp->preg, errbuf, sizeof errbuf);
- fprintf(stderr, "%s: unable to match %s against %s: %s\n",
- __progname, name, sp->pattern, errbuf);
- return -1;
- }
-
- /* Cound matches found. */
- if (i >= 0)
- spec_arr[i].matches++;
-
- return i;
-}
-
-/* Used with qsort to sort specs from lowest to highest hasMetaChars value */
-static int spec_compare(const void* specA, const void* specB)
-{
- return (
- ((const spec_t)specB)->hasMetaChars -
- ((const spec_t)specA)->hasMetaChars
- );
-}
-
-/*
- * Check for duplicate specifications. If a duplicate specification is found
- * and the context is the same, give a warning to the user. If a duplicate
- * specification is found and the context is different, give a warning
- * to the user (This could be changed to error). Return of non-zero is an error.
- */
-static int nodups_specs(void)
-{
- int i, j;
-
- for (i = 0; i < nspec; i++) {
- spec_t sip = spec_arr + i;
- for (j = i + 1; j < nspec; j++) {
- spec_t sjp = spec_arr + j;
-
- /* Check if same RE string */
- if (strcmp(sjp->pattern, sip->pattern))
- continue;
- if (sjp->mode && sip->mode && sjp->mode != sip->mode)
- continue;
-
- /* Same RE string found */
- if (strcmp(sjp->context, sip->context)) {
- /* If different contexts, give warning */
- fprintf(stderr,
- "ERROR: Multiple different specifications for %s (%s and %s).\n",
- sip->pattern, sjp->context, sip->context);
- } else {
- /* If same contexts give warning */
- fprintf(stderr,
- "WARNING: Multiple same specifications for %s.\n",
- sip->pattern);
- }
- }
- }
- return 0;
-}
-
-static void usage(const char * const name, poptContext optCon)
-{
- fprintf(stderr,
- "usage: %s [-dnqvW] spec_file pathname...\n"
- "usage: %s -s [-dnqvW] spec_file\n", name, name);
- poptPrintUsage(optCon, stderr, 0);
- exit(1);
-}
-
-/* Determine if the regular expression specification has any meta characters. */
-static void spec_hasMetaChars(spec_t sp)
-{
- char * c = sp->pattern;
- int len = strlen(c);
- char * end = c + len;
-
- sp->hasMetaChars = 0;
-
- /* Look at each character in the RE specification string for a
- * meta character. Return when any meta character reached. */
- while (c != end) {
- switch(*c) {
- case '.':
- case '^':
- case '$':
- case '?':
- case '*':
- case '+':
- case '|':
- case '[':
- case '(':
- case '{':
- sp->hasMetaChars = 1;
- return;
- break;
- case '\\': /* skip the next character */
- c++;
- break;
- default:
- break;
-
- }
- c++;
- }
- return;
-}
-
-/*
- * Apply the last matching specification to a file.
- * This function is called by nftw on each file during
- * the directory traversal.
- */
-static int apply_spec(const char *file,
- const struct stat *sb_unused, int flag, struct FTW *s_unused)
-{
- const char * my_file;
- file_spec_t fl;
- struct stat my_sb;
- int i, ret;
- char * context;
- spec_t sp;
-
- /* Skip the extra slash at the beginning, if present. */
- if (file[0] == '/' && file[1] == '/')
- my_file = &file[1];
- else
- my_file = file;
-
- if (flag == FTW_DNR) {
- fprintf(stderr, "%s: unable to read directory %s\n",
- __progname, my_file);
- return 0;
- }
-
- i = match(my_file, &my_sb);
- if (i < 0)
- /* No matching specification. */
- return 0;
- sp = spec_arr + i;
-
- /*
- * Try to add an association between this inode and
- * this specification. If there is already an association
- * for this inode and it conflicts with this specification,
- * then use the last matching specification.
- */
- if (add_assoc) {
- fl = file_spec_add(my_sb.st_ino, i, my_file);
- if (!fl)
- /* Insufficient memory to proceed. */
- return 1;
-
- if (fl->specind != i)
- /* There was already an association and it took precedence. */
- return 0;
- }
-
- if (debug) {
- if (sp->type_str) {
- printf("%s: %s matched by (%s,%s,%s)\n", __progname,
- my_file, sp->pattern,
- sp->type_str, sp->context);
- } else {
- printf("%s: %s matched by (%s,%s)\n", __progname,
- my_file, sp->pattern, sp->context);
- }
- }
-
- /* Get the current context of the file. */
- ret = lgetfilecon(my_file, &context);
- if (ret < 0) {
- if (errno == ENODATA) {
- context = xstrdup("<<none>>");
- } else {
- perror(my_file);
- fprintf(stderr, "%s: unable to obtain attribute for file %s\n",
- __progname, my_file);
- return -1;
- }
- }
-
- /*
- * Do not relabel the file if the matching specification is
- * <<none>> or the file is already labeled according to the
- * specification.
- */
- if ((strcmp(sp->context, "<<none>>") == 0) ||
- (strcmp(sp->context, context) == 0))
- {
- freecon(context);
- return 0;
- }
-
- if (verbose) {
- printf("%s: relabeling %s from %s to %s\n", __progname,
- my_file, context, sp->context);
- }
-
- freecon(context);
-
- /*
- * Do not relabel the file if -n was used.
- */
- if (!change)
- return 0;
-
- /*
- * Relabel the file to the specified context.
- */
- ret = lsetfilecon(my_file, sp->context);
- if (ret) {
- perror(my_file);
- fprintf(stderr, "%s: unable to relabel %s to %s\n",
- __progname, my_file, sp->context);
- return 1;
- }
-
- return 0;
-}
-
-static int nerr = 0;
-
-static void inc_err(void)
-{
- nerr++;
- if (nerr > 9 && !debug) {
- fprintf(stderr, "Exiting after 10 errors.\n");
- exit(1);
- }
-}
-
-static
-int parseREContexts(const char *fn)
-{
- FILE * fp;
- char errbuf[255 + 1];
- char buf[255 + 1];
- char * bp;
- char * regex;
- char * type;
- char * context;
- char * anchored_regex;
- int items;
- int len;
- int lineno;
- int pass;
- int regerr;
- spec_t sp;
-
- if ((fp = fopen(fn, "r")) == NULL) {
- perror(fn);
- return -1;
- }
-
- /*
- * Perform two passes over the specification file.
- * The first pass counts the number of specifications and
- * performs simple validation of the input. At the end
- * of the first pass, the spec array is allocated.
- * The second pass performs detailed validation of the input
- * and fills in the spec array.
- */
- for (pass = 0; pass < 2; pass++) {
- lineno = 0;
- nspec = 0;
- sp = spec_arr;
- while (fgets(buf, sizeof buf, fp)) {
- lineno++;
- len = strlen(buf);
- if (buf[len - 1] != '\n') {
- fprintf(stderr,
- "%s: no newline on line number %d (only read %s)\n",
- fn, lineno, buf);
- inc_err();
- continue;
- }
- buf[len - 1] = 0;
- bp = buf;
- while (isspace(*bp))
- bp++;
- /* Skip comment lines and empty lines. */
- if (*bp == '#' || *bp == 0)
- continue;
- items = sscanf(buf, "%as %as %as", ®ex, &type, &context);
- if (items < 2) {
- fprintf(stderr,
- "%s: line number %d is missing fields (only read %s)\n",
- fn, lineno, buf);
- inc_err();
- if (items == 1)
- free(regex);
- continue;
- } else if (items == 2) {
- /* The type field is optional. */
- free(context);
- context = type;
- type = 0;
- }
-
- if (pass == 1) {
- /* On the second pass, compile and store the specification in spec. */
- const char *reg_buf = regex;
- sp->stem_id = find_stem_from_spec(®_buf);
- sp->pattern = regex;
-
- /* Anchor the regular expression. */
- len = strlen(reg_buf);
- anchored_regex = xmalloc(len + 3);
- sprintf(anchored_regex, "^%s$", reg_buf);
-
- /* Compile the regular expression. */
- sp->preg = xcalloc(1, sizeof(*sp->preg));
- regerr = regcomp(sp->preg, anchored_regex,
- REG_EXTENDED | REG_NOSUB);
- if (regerr < 0) {
- regerror(regerr, sp->preg, errbuf, sizeof errbuf);
- fprintf(stderr,
- "%s: unable to compile regular expression %s on line number %d: %s\n",
- fn, regex, lineno,
- errbuf);
- inc_err();
- }
- free(anchored_regex);
-
- /* Convert the type string to a mode format */
- sp->type_str = type;
- sp->mode = 0;
- if (!type)
- goto skip_type;
- len = strlen(type);
- if (type[0] != '-' || len != 2) {
- fprintf(stderr,
- "%s: invalid type specifier %s on line number %d\n",
- fn, type, lineno);
- inc_err();
- goto skip_type;
- }
- switch (type[1]) {
- case 'b':
- sp->mode = S_IFBLK;
- break;
- case 'c':
- sp->mode = S_IFCHR;
- break;
- case 'd':
- sp->mode = S_IFDIR;
- break;
- case 'p':
- sp->mode = S_IFIFO;
- break;
- case 'l':
- sp->mode = S_IFLNK;
- break;
- case 's':
- sp->mode = S_IFSOCK;
- break;
- case '-':
- sp->mode = S_IFREG;
- break;
- default:
- fprintf(stderr,
- "%s: invalid type specifier %s on line number %d\n",
- fn, type, lineno);
- inc_err();
- }
-
- skip_type:
-
- sp->context = context;
-
- if (strcmp(context, "<<none>>")) {
- if (security_check_context(context) < 0 && errno != ENOENT) {
- fprintf(stderr,
- "%s: invalid context %s on line number %d\n",
- fn, context, lineno);
- inc_err();
- }
- }
-
- /* Determine if specification has
- * any meta characters in the RE */
- spec_hasMetaChars(sp);
- sp++;
- }
-
- nspec++;
- if (pass == 0) {
- free(regex);
- if (type)
- free(type);
- free(context);
- }
- }
-
- if (nerr)
- return -1;
-
- if (pass == 0) {
- QPRINTF("%s: read %d specifications\n", fn, nspec);
- if (nspec == 0)
- return 0;
- spec_arr = xcalloc(nspec, sizeof(*spec_arr));
- rewind(fp);
- }
- }
- fclose(fp);
-
- /* Sort the specifications with most general first */
- qsort(spec_arr, nspec, sizeof(*spec_arr), spec_compare);
-
- /* Verify no exact duplicates */
- if (nodups_specs() != 0)
- return -1;
- return 0;
-}
-
-static struct poptOption optionsTable[] = {
- { "debug", 'd', POPT_ARG_VAL, &debug, 1,
- N_("show what specification matched each file"), NULL },
- { "nochange", 'n', POPT_ARG_VAL, &change, 0,
- N_("do not change any file labels"), NULL },
- { "quiet", 'q', POPT_ARG_VAL, &quiet, 1,
- N_("be quiet (suppress non-error output)"), NULL },
- { "root", 'r', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &rootpath, 0,
- N_("use an alternate root path"), N_("ROOT") },
- { "stdin", 's', POPT_ARG_VAL, &use_stdin, 1,
- N_("use stdin for a list of files instead of searching a partition"), NULL },
- { "verbose", 'v', POPT_ARG_VAL, &warn_no_match, 1,
- N_("show changes in file labels"), NULL },
- { "warn", 'W', POPT_ARG_VAL, &warn_no_match, 1,
- N_("warn about entries that have no matching file"), NULL },
-
- POPT_AUTOHELP
- POPT_TABLEEND
-};
-
-int main(int argc, char **argv)
-{
- poptContext optCon;
- const char ** av;
- const char * arg;
- int ec = EXIT_FAILURE; /* assume failure. */
- int rc;
- int i;
-
-#if HAVE_MCHECK_H && HAVE_MTRACE
- /*@-noeffect@*/
- mtrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
- /*@=noeffect@*/
-#endif
-
- setprogname(argv[0]); /* Retrofit glibc __progname */
- /* XXX glibc churn sanity */
- if (__progname == NULL) {
- if ((__progname = strrchr(argv[0], '/')) != NULL) __progname++;
- else __progname = argv[0];
- }
-
- (void) setlocale(LC_ALL, "" );
- (void) bindtextdomain(PACKAGE, LOCALEDIR);
- (void) textdomain(PACKAGE);
-
- optCon = poptGetContext(__progname, argc, (const char **)argv, optionsTable, 0);
-
- /* Process all options, whine if unknown. */
- while ((rc = poptGetNextOpt(optCon)) > 0) {
- switch (rc) {
- default:
-/*@-nullpass@*/
- fprintf(stderr, _("%s: option table misconfigured (%d)\n"),
- __progname, rc);
-/*@=nullpass@*/
- goto exit;
- /*@notreached@*/ /*@switchbreak@*/ break;
- }
- }
-
- if (rc < -1) {
-/*@-nullpass@*/
- fprintf(stderr, "%s: %s: %s\n", __progname,
- poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
- poptStrerror(rc));
-/*@=nullpass@*/
- goto exit;
- }
-
- /* trim trailing /, if present */
- if (rootpath != NULL) {
- rootpathlen = strlen(rootpath);
- while (rootpath[rootpathlen - 1] == '/')
- rootpath[--rootpathlen] = 0;
- }
-
- av = poptGetArgs(optCon);
- if (use_stdin) {
- add_assoc = 0;
- /* Check exactly 1 arg */
- if (av == NULL || !(av[0] != NULL && av[1] == NULL)) {
- usage(__progname, optCon);
- }
- } else {
- /* Check at least 2 args */
- if (av == NULL || !(av[0] != NULL && av[1] != NULL))
- usage(__progname, optCon);
- }
-
- /* Parse the specification file. */
- if (parseREContexts(*av) != 0) {
- perror(*av);
- goto exit;
- }
- av++;
-
- /*
- * Apply the specifications to the file systems.
- */
- if (use_stdin) {
- char buf[PATH_MAX];
- struct stat sb;
- int flag;
-
- while(fgets(buf, sizeof(buf), stdin)) {
- strtok(buf, "\n");
- if (buf[0] == '\n')
- continue;
- if (stat(buf, &sb)) {
- fprintf(stderr, "File \"%s\" not found.\n", buf);
- continue;
- }
- switch(sb.st_mode) {
- case S_IFDIR:
- flag = FTW_D;
- break;
- case S_IFLNK:
- flag = FTW_SL;
- break;
- default:
- flag = FTW_F;
- break;
- }
- apply_spec(buf, &sb, flag, NULL);
- }
- }
- else while ((arg = *av++) != NULL)
- {
- if (rootpath != NULL) {
- QPRINTF("%s: labeling files, pretending %s is /\n",
- __progname, rootpath);
- }
-
- QPRINTF("%s: labeling files under %s\n", __progname, arg);
-
- /* Walk the file tree, calling apply_spec on each file. */
- if (nftw(arg, apply_spec, 1024, FTW_PHYS | FTW_MOUNT)) {
- fprintf(stderr, "%s: error while labeling files under %s\n",
- __progname, arg);
- goto exit;
- }
-
- /*
- * Evaluate the association hash table distribution for the
- * directory tree just traversed.
- */
- file_spec_eval();
-
- /* Reset the association hash table for the next directory tree. */
- file_spec_destroy();
- }
-
- if (warn_no_match) {
- for (i = 0; i < nspec; i++) {
- spec_t sp;
-
- sp = spec_arr + i;
- if (sp->matches > 0)
- continue;
- if (sp->type_str) {
- printf("%s: Warning! No matches for (%s, %s, %s)\n",
- __progname, sp->pattern,
- sp->type_str, sp->context);
- } else {
- printf("%s: Warning! No matches for (%s, %s)\n",
- __progname, sp->pattern, sp->context);
- }
- }
- }
-
- QPRINTF("%s: Done.\n", __progname);
- ec = 0;
-
-exit:
- optCon = poptFreeContext(optCon);
-
-#if HAVE_MCHECK_H && HAVE_MTRACE
- /*@-noeffect@*/
- muntrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
- /*@=noeffect@*/
-#endif
-
- return ec;
-}