On 10/29/09 10:05 AM, "Daniel J Walsh" <dwalsh@xxxxxxxxxx> wrote: > On 10/28/2009 04:03 PM, Chad Sellers wrote: >> I've rebased this so that it will apply to current trunk (which is included >> below). Looks pretty good to me, though I have 2 questions: >> >> 1) Why remove the free() call in remove_exclude()? > I have no idea why this would be removed. I think it should be put back in. OK, I put it back. >> 2) Why the new check at the beginning of match()? >> > restorecond and setfiles/restorecon should realize when they hit a hardlinked > file with multiple labels and not label them. OK, I left this in. I did change the message slightly. This is now merged upstream with these changes. Thanks, Chad Sellers > I actually think the code should check every file and if it has hard links, > not relabel the link until it finishes the search. Then check all the links > and see if they all have the same label. Then set the label. >> Other than that, let me know if anyone spots a rebasing error. >> >> Rebased patch: >> >> This is the first of two patches. >> >> This patch splits all of the restore functionality in setfiles >> into another two files, restore.c and restore.h. >> >> The reason for this is shown in the next patch, which patches >> restorecond to share this code. >> >> To use it, instantiate a restore_opts struct with the proper options >> and then pass a pointer to it into restore_init, and call restore_destroy >> later. >> >> Signed-off-by: Thomas Liu <tliu@xxxxxxxxxx> >> Signed-off-by: Dan Walsh <dwalsh@xxxxxxxxxx> >> >> --- >> policycoreutils/setfiles/Makefile | 4 +- >> policycoreutils/setfiles/restore.c | 605 ++++++++++++++++++++++++++++ >> policycoreutils/setfiles/restore.h | 50 +++ >> policycoreutils/setfiles/setfiles.c | 741 >> ++++------------------------------- >> 4 files changed, 735 insertions(+), 665 deletions(-) >> create mode 100644 policycoreutils/setfiles/restore.c >> create mode 100644 policycoreutils/setfiles/restore.h >> >> diff --git a/policycoreutils/setfiles/Makefile >> b/policycoreutils/setfiles/Makefile >> index 8600f58..26a965f 100644 >> --- a/policycoreutils/setfiles/Makefile >> +++ b/policycoreutils/setfiles/Makefile >> @@ -5,7 +5,7 @@ MANDIR = $(PREFIX)/share/man >> LIBDIR ?= $(PREFIX)/lib >> AUDITH = $(shell ls /usr/include/libaudit.h 2>/dev/null) >> >> -CFLAGS = -Werror -Wall -W >> +CFLAGS = -g -Werror -Wall -W >> override CFLAGS += -I$(PREFIX)/include >> LDLIBS = -lselinux -lsepol -L$(LIBDIR) >> >> @@ -16,7 +16,7 @@ endif >> >> all: setfiles restorecon >> >> -setfiles: setfiles.o >> +setfiles: setfiles.o restore.o >> >> restorecon: setfiles >> ln -sf setfiles restorecon >> diff --git a/policycoreutils/setfiles/restore.c >> b/policycoreutils/setfiles/restore.c >> new file mode 100644 >> index 0000000..d37fe22 >> --- /dev/null >> +++ b/policycoreutils/setfiles/restore.c >> @@ -0,0 +1,605 @@ >> +#include "restore.h" >> + >> +#define SKIP -2 >> +#define ERR -1 >> +#define MAX_EXCLUDES 1000 >> + >> +/* >> + * 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) >> + >> +/* >> + * An association between an inode and a context. >> + */ >> +typedef struct file_spec { >> + ino_t ino; /* inode number */ >> + char *con; /* matched context */ >> + char *file; /* full pathname */ >> + struct file_spec *next; /* next association in hash bucket chain */ >> +} file_spec_t; >> + >> +struct edir { >> + char *directory; >> + size_t size; >> +}; >> + >> + >> +static file_spec_t *fl_head; >> +static int exclude(const char *file); >> +static int filespec_add(ino_t ino, const security_context_t con, const char >> *file); >> +static int only_changed_user(const char *a, const char *b); >> +struct restore_opts *r_opts = NULL; >> +static void filespec_destroy(void); >> +static void filespec_eval(void); >> +static int excludeCtr = 0; >> +static struct edir excludeArray[MAX_EXCLUDES]; >> + >> +void remove_exclude(const char *directory) >> +{ >> + int i = 0; >> + for (i = 0; i < excludeCtr; i++) { >> + if (strcmp(directory, excludeArray[i].directory) == 0) { >> + if (i != excludeCtr-1) >> + excludeArray[i] = excludeArray[excludeCtr-1]; >> + excludeCtr--; >> + return; >> + } >> + } >> + return; >> + >> +} >> + >> +void restore_init(struct restore_opts *opts) >> +{ >> + r_opts = opts; >> + struct selinux_opt selinux_opts[] = { >> + { SELABEL_OPT_VALIDATE, r_opts->selabel_opt_validate }, >> + { SELABEL_OPT_PATH, r_opts->selabel_opt_path } >> + }; >> + r_opts->hnd = selabel_open(SELABEL_CTX_FILE, selinux_opts, 2); >> + if (!r_opts->hnd) { >> + perror(r_opts->selabel_opt_path); >> + exit(1); >> + } >> +} >> + >> +void restore_finish() >> +{ >> + int i; >> + for (i = 0; i < excludeCtr; i++) { >> + free(excludeArray[i].directory); >> + } >> +} >> + >> +static int match(const char *name, struct stat *sb, char **con) >> +{ >> + if (!(r_opts->hard_links) && !S_ISDIR(sb->st_mode) && (sb->st_nlink > 1)) { >> + fprintf(stderr, "Warning! %s refers to a hard link, not fixing hard >> links.\n", >> + name); >> + return -1; >> + } >> + >> + if (NULL != r_opts->rootpath) { >> + if (0 != strncmp(r_opts->rootpath, name, r_opts->rootpathlen)) { >> + fprintf(stderr, "%s: %s is not located in %s\n", >> + r_opts->progname, name, r_opts->rootpath); >> + return -1; >> + } >> + name += r_opts->rootpathlen; >> + } >> + >> + if (r_opts->rootpath != NULL && name[0] == '\0') >> + /* this is actually the root dir of the alt root */ >> + return selabel_lookup_raw(r_opts->hnd, con, "/", sb->st_mode); >> + else >> + return selabel_lookup_raw(r_opts->hnd, con, name, sb->st_mode); >> +} >> +static int restore(FTSENT *ftsent) >> +{ >> + char *my_file = strdupa(ftsent->fts_path); >> + int ret; >> + char *context, *newcon; >> + int user_only_changed = 0; >> + >> + if (match(my_file, ftsent->fts_statp, &newcon) < 0) >> + /* Check for no matching specification. */ >> + return (errno == ENOENT) ? 0 : -1; >> + >> + if (r_opts->progress) { >> + r_opts->count++; >> + if (r_opts->count % (80 * STAR_COUNT) == 0) { >> + fprintf(stdout, "\n"); >> + fflush(stdout); >> + } >> + if (r_opts->count % STAR_COUNT == 0) { >> + fprintf(stdout, "*"); >> + fflush(stdout); >> + } >> + } >> + >> + /* >> + * 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 (r_opts->add_assoc) { >> + ret = filespec_add(ftsent->fts_statp->st_ino, newcon, my_file); >> + if (ret < 0) >> + goto err; >> + >> + if (ret > 0) >> + /* There was already an association and it took precedence. */ >> + goto out; >> + } >> + >> + if (r_opts->debug) { >> + printf("%s: %s matched by %s\n", r_opts->progname, my_file, newcon); >> + } >> + >> + /* Get the current context of the file. */ >> + ret = lgetfilecon_raw(ftsent->fts_accpath, &context); >> + if (ret < 0) { >> + if (errno == ENODATA) { >> + context = NULL; >> + } else { >> + fprintf(stderr, "%s get context on %s failed: '%s'\n", >> + r_opts->progname, my_file, strerror(errno)); >> + goto err; >> + } >> + user_only_changed = 0; >> + } else >> + user_only_changed = only_changed_user(context, newcon); >> + /* lgetfilecon returns number of characters and ret needs to be reset >> + * to 0. >> + */ >> + ret = 0; >> + >> + /* >> + * Do not relabel the file if the matching specification is >> + * <<none>> or the file is already labeled according to the >> + * specification. >> + */ >> + if ((strcmp(newcon, "<<none>>") == 0) || >> + (context && (strcmp(context, newcon) == 0))) { >> + freecon(context); >> + goto out; >> + } >> + >> + if (!r_opts->force && context && (is_context_customizable(context) > 0)) { >> + if (r_opts->verbose > 1) { >> + fprintf(stderr, >> + "%s: %s not reset customized by admin to %s\n", >> + r_opts->progname, my_file, context); >> + } >> + freecon(context); >> + goto out; >> + } >> + >> + if (r_opts->verbose) { >> + /* If we're just doing "-v", trim out any relabels where >> + * the user has r_opts->changed but the role and type are the >> + * same. For "-vv", emit everything. */ >> + if (r_opts->verbose > 1 || !user_only_changed) { >> + printf("%s reset %s context %s->%s\n", >> + r_opts->progname, my_file, context ?: "", newcon); >> + } >> + } >> + >> + if (r_opts->logging && !user_only_changed) { >> + if (context) >> + syslog(LOG_INFO, "relabeling %s from %s to %s\n", >> + my_file, context, newcon); >> + else >> + syslog(LOG_INFO, "labeling %s to %s\n", >> + my_file, newcon); >> + } >> + >> + if (r_opts->outfile && !user_only_changed) >> + fprintf(r_opts->outfile, "%s\n", my_file); >> + >> + if (context) >> + freecon(context); >> + >> + /* >> + * Do not relabel the file if -n was used. >> + */ >> + if (!r_opts->change || user_only_changed) >> + goto out; >> + >> + /* >> + * Relabel the file to the specified context. >> + */ >> + ret = lsetfilecon(ftsent->fts_accpath, newcon); >> + if (ret) { >> + fprintf(stderr, "%s set context %s->%s failed:'%s'\n", >> + r_opts->progname, my_file, newcon, strerror(errno)); >> + goto skip; >> + } >> + ret = 1; >> +out: >> + freecon(newcon); >> + return ret; >> +skip: >> + freecon(newcon); >> + return SKIP; >> +err: >> + freecon(newcon); >> + return ERR; >> +} >> +/* >> + * Apply the last matching specification to a file. >> + * This function is called by fts on each file during >> + * the directory traversal. >> + */ >> +static int apply_spec(FTSENT *ftsent) >> +{ >> + if (ftsent->fts_info == FTS_DNR) { >> + fprintf(stderr, "%s: unable to read directory %s\n", >> + r_opts->progname, ftsent->fts_path); >> + return SKIP; >> + } >> + >> + int rc = restore(ftsent); >> + if (rc == ERR) { >> + if (!r_opts->abort_on_error) >> + return SKIP; >> + } >> + return rc; >> +} >> + >> +static int symlink_realpath(char *name, char *path) >> +{ >> + char *p = NULL, *file_sep; >> + char *tmp_path = strdupa(name); >> + size_t len = 0; >> + >> + if (!tmp_path) { >> + fprintf(stderr, "strdupa on %s failed: %s\n", name, >> + strerror(errno)); >> + return -1; >> + } >> + file_sep = strrchr(tmp_path, '/'); >> + if (file_sep == tmp_path) { >> + file_sep++; >> + p = strcpy(path, ""); >> + } else if (file_sep) { >> + *file_sep = 0; >> + file_sep++; >> + p = realpath(tmp_path, path); >> + } else { >> + file_sep = tmp_path; >> + p = realpath("./", path); >> + } >> + if (p) >> + len = strlen(p); >> + if (!p || len + strlen(file_sep) + 2 > PATH_MAX) { >> + fprintf(stderr, "symlink_realpath(%s) failed %s\n", name, >> + strerror(errno)); >> + return -1; >> + } >> + p += len; >> + /* ensure trailing slash of directory name */ >> + if (len == 0 || *(p - 1) != '/') { >> + *p = '/'; >> + p++; >> + } >> + strcpy(p, file_sep); >> + return 0; >> +} >> + >> +static int process_one(char *name, int recurse_this_path) >> +{ >> + int rc = 0; >> + const char *namelist[2] = {name, NULL}; >> + dev_t dev_num = 0; >> + FTS *fts_handle; >> + FTSENT *ftsent; >> + >> + fts_handle = fts_open((char **)namelist, r_opts->fts_flags, NULL); >> + if (fts_handle == NULL) { >> + fprintf(stderr, >> + "%s: error while labeling %s: %s\n", >> + r_opts->progname, namelist[0], strerror(errno)); >> + goto err; >> + } >> + >> + >> + ftsent = fts_read(fts_handle); >> + if (ftsent != NULL) { >> + /* Keep the inode of the first one. */ >> + dev_num = ftsent->fts_statp->st_dev; >> + } >> + >> + do { >> + rc = 0; >> + /* Skip the post order nodes. */ >> + if (ftsent->fts_info == FTS_DP) >> + continue; >> + /* If the XDEV flag is set and the device is different */ >> + if (ftsent->fts_statp->st_dev != dev_num && >> + FTS_XDEV == (r_opts->fts_flags & FTS_XDEV)) >> + continue; >> + if (excludeCtr > 0) { >> + if (exclude(ftsent->fts_path)) { >> + fts_set(fts_handle, ftsent, FTS_SKIP); >> + continue; >> + } >> + } >> + rc = apply_spec(ftsent); >> + if (rc == SKIP) >> + fts_set(fts_handle, ftsent, FTS_SKIP); >> + if (rc == ERR) >> + goto err; >> + if (!recurse_this_path) >> + break; >> + } while ((ftsent = fts_read(fts_handle)) != NULL); >> + >> +out: >> + if (r_opts->add_assoc) { >> + if (!r_opts->quiet) >> + filespec_eval(); >> + filespec_destroy(); >> + } >> + if (fts_handle) >> + fts_close(fts_handle); >> + return rc; >> + >> +err: >> + rc = -1; >> + goto out; >> +} >> + >> +int process_one_realpath(char *name, int recurse) >> +{ >> + int rc = 0; >> + char *p; >> + struct stat sb; >> + >> + if (r_opts == NULL){ >> + fprintf(stderr, >> + "Must call initialize first!"); >> + return -1; >> + } >> + >> + if (!r_opts->expand_realpath) { >> + return process_one(name, recurse); >> + } else { >> + rc = lstat(name, &sb); >> + if (rc < 0) { >> + fprintf(stderr, "%s: lstat(%s) failed: %s\n", >> + r_opts->progname, name, strerror(errno)); >> + return -1; >> + } >> + >> + if (S_ISLNK(sb.st_mode)) { >> + char path[PATH_MAX + 1]; >> + >> + rc = symlink_realpath(name, path); >> + if (rc < 0) >> + return rc; >> + rc = process_one(path, 0); >> + if (rc < 0) >> + return rc; >> + >> + p = realpath(name, NULL); >> + if (p) { >> + rc = process_one(p, recurse); >> + free(p); >> + } >> + return rc; >> + } else { >> + p = realpath(name, NULL); >> + if (!p) { >> + fprintf(stderr, "realpath(%s) failed %s\n", name, >> + strerror(errno)); >> + return -1; >> + } >> + rc = process_one(p, recurse); >> + free(p); >> + return rc; >> + } >> + } >> +} >> + >> +static int exclude(const char *file) >> +{ >> + int i = 0; >> + for (i = 0; i < excludeCtr; i++) { >> + if (strncmp >> + (file, excludeArray[i].directory, >> + excludeArray[i].size) == 0) { >> + if (file[excludeArray[i].size] == 0 >> + || file[excludeArray[i].size] == '/') { >> + return 1; >> + } >> + } >> + } >> + return 0; >> +} >> + >> +int add_exclude(const char *directory) >> +{ >> + size_t len = 0; >> + >> + if (directory == NULL || directory[0] != '/') { >> + fprintf(stderr, "Full path required for exclude: %s.\n", >> + directory); >> + return 1; >> + } >> + if (excludeCtr == MAX_EXCLUDES) { >> + fprintf(stderr, "Maximum excludes %d exceeded.\n", >> + MAX_EXCLUDES); >> + return 1; >> + } >> + >> + len = strlen(directory); >> + while (len > 1 && directory[len - 1] == '/') { >> + len--; >> + } >> + excludeArray[excludeCtr].directory = strndup(directory, len); >> + >> + if (excludeArray[excludeCtr].directory == NULL) { >> + fprintf(stderr, "Out of memory.\n"); >> + return 1; >> + } >> + excludeArray[excludeCtr++].size = len; >> + >> + return 0; >> +} >> + >> +/* Compare two contexts to see if their differences are "significant", >> + * or whether the only difference is in the user. */ >> +static int only_changed_user(const char *a, const char *b) >> +{ >> + char *rest_a, *rest_b; /* Rest of the context after the user */ >> + if (r_opts->force) >> + return 0; >> + if (!a || !b) >> + return 0; >> + rest_a = strchr(a, ':'); >> + rest_b = strchr(b, ':'); >> + if (!rest_a || !rest_b) >> + return 0; >> + return (strcmp(rest_a, rest_b) == 0); >> +} >> + >> +/* >> + * Evaluate the association hash table distribution. >> + */ >> +static void filespec_eval(void) >> +{ >> + file_spec_t *fl; >> + int h, used, nel, len, longest; >> + >> + if (!fl_head) >> + return; >> + >> + 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; >> + } >> + >> + if (r_opts->verbose > 1) >> + printf >> + ("%s: hash table stats: %d elements, %d/%d buckets used, longest >> chain length %d\n", >> + __FUNCTION__, nel, used, HASH_BUCKETS, longest); >> +} >> + >> +/* >> + * Destroy the association hash table. >> + */ >> +static void filespec_destroy(void) >> +{ >> + file_spec_t *fl, *tmp; >> + int h; >> + >> + if (!fl_head) >> + return; >> + >> + for (h = 0; h < HASH_BUCKETS; h++) { >> + fl = fl_head[h].next; >> + while (fl) { >> + tmp = fl; >> + fl = fl->next; >> + freecon(tmp->con); >> + free(tmp->file); >> + free(tmp); >> + } >> + fl_head[h].next = NULL; >> + } >> + free(fl_head); >> + fl_head = NULL; >> +} >> +/* >> + * Try to add an association between an inode and a context. >> + * If there is a different context that matched the inode, >> + * then use the first context that matched. >> + */ >> +static int filespec_add(ino_t ino, const security_context_t con, const char >> *file) >> +{ >> + file_spec_t *prevfl, *fl; >> + int h, ret; >> + struct stat sb; >> + >> + if (!fl_head) { >> + fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); >> + if (!fl_head) >> + goto oom; >> + memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS); >> + } >> + >> + h = (ino + (ino >> HASH_BITS)) & HASH_MASK; >> + for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; >> + prevfl = fl, fl = fl->next) { >> + if (ino == fl->ino) { >> + ret = lstat(fl->file, &sb); >> + if (ret < 0 || sb.st_ino != ino) { >> + freecon(fl->con); >> + free(fl->file); >> + fl->file = strdup(file); >> + if (!fl->file) >> + goto oom; >> + fl->con = strdup(con); >> + if (!fl->con) >> + goto oom; >> + return 1; >> + } >> + >> + if (strcmp(fl->con, con) == 0) >> + return 1; >> + >> + fprintf(stderr, >> + "%s: conflicting specifications for %s and %s, using %s.\n", >> + __FUNCTION__, file, fl->file, fl->con); >> + free(fl->file); >> + fl->file = strdup(file); >> + if (!fl->file) >> + goto oom; >> + return 1; >> + } >> + >> + if (ino > fl->ino) >> + break; >> + } >> + >> + fl = malloc(sizeof(file_spec_t)); >> + if (!fl) >> + goto oom; >> + fl->ino = ino; >> + fl->con = strdup(con); >> + if (!fl->con) >> + goto oom_freefl; >> + fl->file = strdup(file); >> + if (!fl->file) >> + goto oom_freefl; >> + fl->next = prevfl->next; >> + prevfl->next = fl; >> + return 0; >> + oom_freefl: >> + free(fl); >> + oom: >> + fprintf(stderr, >> + "%s: insufficient memory for file label entry for %s\n", >> + __FUNCTION__, file); >> + return -1; >> +} >> + >> + >> + >> diff --git a/policycoreutils/setfiles/restore.h >> b/policycoreutils/setfiles/restore.h >> new file mode 100644 >> index 0000000..03b82e8 >> --- /dev/null >> +++ b/policycoreutils/setfiles/restore.h >> @@ -0,0 +1,50 @@ >> +#ifndef RESTORE_H >> +#define RESTORE_H >> +#ifndef _GNU_SOURCE >> +#define _GNU_SOURCE >> +#endif >> +#include <fts.h> >> +#include <errno.h> >> +#include <string.h> >> +#include <stdio.h> >> +#include <syslog.h> >> +#include <sys/stat.h> >> +#include <sepol/sepol.h> >> +#include <selinux/selinux.h> >> +#include <selinux/label.h> >> +#include <stdlib.h> >> +#include <limits.h> >> + >> +#define STAR_COUNT 1000 >> + >> +/* Things that need to be init'd */ >> +struct restore_opts { >> + int add_assoc; /* Track inode associations for conflict detection. */ >> + int progress; >> + unsigned long long count; >> + int debug; >> + int change; >> + int hard_links; >> + int verbose; >> + int logging; >> + char *rootpath; >> + int rootpathlen; >> + char *progname; >> + FILE *outfile; >> + int force; >> + struct selabel_handle *hnd; >> + int expand_realpath; /* Expand paths via realpath. */ >> + int abort_on_error; /* Abort the file tree walk upon an error. */ >> + int quiet; >> + int fts_flags; /* Flags to fts, e.g. follow links, follow mounts */ >> + const char *selabel_opt_validate; >> + const char *selabel_opt_path; >> +}; >> + >> +void restore_init(struct restore_opts *opts); >> +void restore_finish(); >> +int add_exclude(const char *directory); >> +void remove_exclude(const char *directory); >> +int process_one_realpath(char *name, int recurse); >> + >> +#endif >> diff --git a/policycoreutils/setfiles/setfiles.c >> b/policycoreutils/setfiles/setfiles.c >> index db2857f..8f4f663 100644 >> --- a/policycoreutils/setfiles/setfiles.c >> +++ b/policycoreutils/setfiles/setfiles.c >> @@ -1,26 +1,12 @@ >> -#ifndef _GNU_SOURCE >> -#define _GNU_SOURCE >> -#endif >> +#include "restore.h" >> #include <unistd.h> >> -#include <stdlib.h> >> #include <fcntl.h> >> -#include <stdio.h> >> #include <stdio_ext.h> >> -#include <string.h> >> -#include <errno.h> >> #include <ctype.h> >> #include <regex.h> >> #include <sys/vfs.h> >> #include <sys/utsname.h> >> #define __USE_XOPEN_EXTENDED 1 /* nftw */ >> -#define SKIP -2 >> -#define ERR -1 >> -#include <fts.h> >> -#include <limits.h> >> -#include <sepol/sepol.h> >> -#include <selinux/selinux.h> >> -#include <selinux/label.h> >> -#include <syslog.h> >> #include <libgen.h> >> #ifdef USE_AUDIT >> #include <libaudit.h> >> @@ -32,287 +18,28 @@ >> static int mass_relabel; >> static int mass_relabel_errs; >> >> -#define STAR_COUNT 1000 >> - >> -static FILE *outfile = NULL; >> -static int force = 0; >> -#define STAT_BLOCK_SIZE 1 >> -static int progress = 0; >> -static unsigned long long count = 0; >> >> -#define MAX_EXCLUDES 1000 >> -static int excludeCtr = 0; >> -struct edir { >> - char *directory; >> - size_t size; >> -}; >> -static struct edir excludeArray[MAX_EXCLUDES]; >> +/* cmdline opts*/ >> >> -/* >> - * Command-line options. >> - */ >> static char *policyfile = NULL; >> -static int debug = 0; >> -static int change = 1; >> -static int quiet = 0; >> -static int ignore_enoent; >> -static int verbose = 0; >> -static int logging = 0; >> static int warn_no_match = 0; >> static int null_terminated = 0; >> -static char *rootpath = NULL; >> -static int rootpathlen = 0; >> -static int recurse; /* Recursive descent. */ >> static int errors; >> +static int ignore_enoent; >> +static struct restore_opts r_opts; >> + >> +#define STAT_BLOCK_SIZE 1 >> + >> >> -static char *progname; >> >> #define SETFILES "setfiles" >> #define RESTORECON "restorecon" >> static int iamrestorecon; >> >> /* Behavior flags determined based on setfiles vs. restorecon */ >> -static int expand_realpath; /* Expand paths via realpath. */ >> -static int abort_on_error; /* Abort the file tree walk upon an error. */ >> -static int add_assoc; /* Track inode associations for conflict detection. */ >> -static int fts_flags; /* Flags to fts, e.g. follow links, follow mounts */ >> static int ctx_validate; /* Validate contexts */ >> static const char *altpath; /* Alternate path to file_contexts */ >> >> -/* Label interface handle */ >> -static struct selabel_handle *hnd; >> - >> -/* >> - * An association between an inode and a context. >> - */ >> -typedef struct file_spec { >> - ino_t ino; /* inode number */ >> - char *con; /* matched context */ >> - char *file; /* full pathname */ >> - struct file_spec *next; /* next association in hash bucket chain */ >> -} file_spec_t; >> - >> -/* >> - * 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 file_spec_t *fl_head; >> - >> -/* >> - * Try to add an association between an inode and a context. >> - * If there is a different context that matched the inode, >> - * then use the first context that matched. >> - */ >> -int filespec_add(ino_t ino, const security_context_t con, const char *file) >> -{ >> - file_spec_t *prevfl, *fl; >> - int h, ret; >> - struct stat sb; >> - >> - if (!fl_head) { >> - fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); >> - if (!fl_head) >> - goto oom; >> - memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS); >> - } >> - >> - h = (ino + (ino >> HASH_BITS)) & HASH_MASK; >> - for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; >> - prevfl = fl, fl = fl->next) { >> - if (ino == fl->ino) { >> - ret = lstat(fl->file, &sb); >> - if (ret < 0 || sb.st_ino != ino) { >> - freecon(fl->con); >> - free(fl->file); >> - fl->file = strdup(file); >> - if (!fl->file) >> - goto oom; >> - fl->con = strdup(con); >> - if (!fl->con) >> - goto oom; >> - return 1; >> - } >> - >> - if (strcmp(fl->con, con) == 0) >> - return 1; >> - >> - fprintf(stderr, >> - "%s: conflicting specifications for %s and %s, using %s.\n", >> - __FUNCTION__, file, fl->file, fl->con); >> - free(fl->file); >> - fl->file = strdup(file); >> - if (!fl->file) >> - goto oom; >> - return 1; >> - } >> - >> - if (ino > fl->ino) >> - break; >> - } >> - >> - fl = malloc(sizeof(file_spec_t)); >> - if (!fl) >> - goto oom; >> - fl->ino = ino; >> - fl->con = strdup(con); >> - if (!fl->con) >> - goto oom_freefl; >> - fl->file = strdup(file); >> - if (!fl->file) >> - goto oom_freefl; >> - fl->next = prevfl->next; >> - prevfl->next = fl; >> - return 0; >> - oom_freefl: >> - free(fl); >> - oom: >> - fprintf(stderr, >> - "%s: insufficient memory for file label entry for %s\n", >> - __FUNCTION__, file); >> - return -1; >> -} >> - >> -/* >> - * Evaluate the association hash table distribution. >> - */ >> -void filespec_eval(void) >> -{ >> - file_spec_t *fl; >> - int h, used, nel, len, longest; >> - >> - if (!fl_head) >> - return; >> - >> - 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; >> - } >> - >> - printf >> - ("%s: hash table stats: %d elements, %d/%d buckets used, longest chain >> length %d\n", >> - __FUNCTION__, nel, used, HASH_BUCKETS, longest); >> -} >> - >> -/* >> - * Destroy the association hash table. >> - */ >> -void filespec_destroy(void) >> -{ >> - file_spec_t *fl, *tmp; >> - int h; >> - >> - if (!fl_head) >> - return; >> - >> - for (h = 0; h < HASH_BUCKETS; h++) { >> - fl = fl_head[h].next; >> - while (fl) { >> - tmp = fl; >> - fl = fl->next; >> - freecon(tmp->con); >> - free(tmp->file); >> - free(tmp); >> - } >> - fl_head[h].next = NULL; >> - } >> - free(fl_head); >> - fl_head = NULL; >> -} >> - >> -static int add_exclude(const char *directory) >> -{ >> - size_t len = 0; >> - >> - if (directory == NULL || directory[0] != '/') { >> - fprintf(stderr, "Full path required for exclude: %s.\n", >> - directory); >> - return 1; >> - } >> - if (excludeCtr == MAX_EXCLUDES) { >> - fprintf(stderr, "Maximum excludes %d exceeded.\n", >> - MAX_EXCLUDES); >> - return 1; >> - } >> - >> - len = strlen(directory); >> - while (len > 1 && directory[len - 1] == '/') { >> - len--; >> - } >> - excludeArray[excludeCtr].directory = strndup(directory, len); >> - >> - if (excludeArray[excludeCtr].directory == NULL) { >> - fprintf(stderr, "Out of memory.\n"); >> - return 1; >> - } >> - excludeArray[excludeCtr++].size = len; >> - >> - return 0; >> -} >> - >> -static void remove_exclude(const char *directory) >> -{ >> - int i = 0; >> - for (i = 0; i < excludeCtr; i++) { >> - if (strcmp(directory, excludeArray[i].directory) == 0) { >> - free(excludeArray[i].directory); >> - if (i != excludeCtr-1) >> - excludeArray[i] = excludeArray[excludeCtr-1]; >> - excludeCtr--; >> - return; >> - } >> - } >> - return; >> -} >> - >> -static int exclude(const char *file) >> -{ >> - int i = 0; >> - for (i = 0; i < excludeCtr; i++) { >> - if (strncmp >> - (file, excludeArray[i].directory, >> - excludeArray[i].size) == 0) { >> - if (file[excludeArray[i].size] == 0 >> - || file[excludeArray[i].size] == '/') { >> - return 1; >> - } >> - } >> - } >> - return 0; >> -} >> - >> -int match(const char *name, struct stat *sb, char **con) >> -{ >> - if (NULL != rootpath) { >> - if (0 != strncmp(rootpath, name, rootpathlen)) { >> - fprintf(stderr, "%s: %s is not located in %s\n", >> - progname, name, rootpath); >> - return -1; >> - } >> - name += rootpathlen; >> - } >> - >> - if (rootpath != NULL && name[0] == '\0') >> - /* this is actually the root dir of the alt root */ >> - return selabel_lookup_raw(hnd, con, "/", sb->st_mode); >> - else >> - return selabel_lookup_raw(hnd, con, name, sb->st_mode); >> -} >> - >> void usage(const char *const name) >> { >> if (iamrestorecon) { >> @@ -334,194 +61,30 @@ static int nerr = 0; >> void inc_err() >> { >> nerr++; >> - if (nerr > 9 && !debug) { >> + if (nerr > 9 && !r_opts.debug) { >> fprintf(stderr, "Exiting after 10 errors.\n"); >> exit(1); >> } >> } >> >> -/* Compare two contexts to see if their differences are "significant", >> - * or whether the only difference is in the user. */ >> -static int only_changed_user(const char *a, const char *b) >> -{ >> - char *rest_a, *rest_b; /* Rest of the context after the user */ >> - if (force) >> - return 0; >> - if (!a || !b) >> - return 0; >> - rest_a = strchr(a, ':'); >> - rest_b = strchr(b, ':'); >> - if (!rest_a || !rest_b) >> - return 0; >> - return (strcmp(rest_a, rest_b) == 0); >> -} >> - >> -static int restore(FTSENT *ftsent) >> -{ >> - char *my_file = strdupa(ftsent->fts_path); >> - int ret; >> - char *context, *newcon; >> - int user_only_changed = 0; >> - >> - if (match(my_file, ftsent->fts_statp, &newcon) < 0) >> - /* Check for no matching specification. */ >> - return (errno == ENOENT) ? 0 : -1; >> - >> - if (progress) { >> - count++; >> - if (count % (80 * STAR_COUNT) == 0) { >> - fprintf(stdout, "\n"); >> - fflush(stdout); >> - } >> - if (count % STAR_COUNT == 0) { >> - fprintf(stdout, "*"); >> - fflush(stdout); >> - } >> - } >> - >> - /* >> - * 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) { >> - ret = filespec_add(ftsent->fts_statp->st_ino, newcon, my_file); >> - if (ret < 0) >> - goto err; >> - >> - if (ret > 0) >> - /* There was already an association and it took precedence. */ >> - goto out; >> - } >> - >> - if (debug) { >> - printf("%s: %s matched by %s\n", progname, my_file, newcon); >> - } >> - >> - /* Get the current context of the file. */ >> - ret = lgetfilecon_raw(ftsent->fts_accpath, &context); >> - if (ret < 0) { >> - if (errno == ENODATA) { >> - context = NULL; >> - } else { >> - fprintf(stderr, "%s get context on %s failed: '%s'\n", >> - progname, my_file, strerror(errno)); >> - goto err; >> - } >> - user_only_changed = 0; >> - } else >> - user_only_changed = only_changed_user(context, newcon); >> - >> - /* >> - * Do not relabel the file if the matching specification is >> - * <<none>> or the file is already labeled according to the >> - * specification. >> - */ >> - if ((strcmp(newcon, "<<none>>") == 0) || >> - (context && (strcmp(context, newcon) == 0))) { >> - freecon(context); >> - goto out; >> - } >> - >> - if (!force && context && (is_context_customizable(context) > 0)) { >> - if (verbose > 1) { >> - fprintf(stderr, >> - "%s: %s not reset customized by admin to %s\n", >> - progname, my_file, context); >> - } >> - freecon(context); >> - goto out; >> - } >> - >> - if (verbose) { >> - /* If we're just doing "-v", trim out any relabels where >> - * the user has changed but the role and type are the >> - * same. For "-vv", emit everything. */ >> - if (verbose > 1 || !user_only_changed) { >> - printf("%s reset %s context %s->%s\n", >> - progname, my_file, context ?: "", newcon); >> - } >> - } >> - >> - if (logging && !user_only_changed) { >> - if (context) >> - syslog(LOG_INFO, "relabeling %s from %s to %s\n", >> - my_file, context, newcon); >> - else >> - syslog(LOG_INFO, "labeling %s to %s\n", >> - my_file, newcon); >> - } >> - >> - if (outfile && !user_only_changed) >> - fprintf(outfile, "%s\n", my_file); >> - >> - if (context) >> - freecon(context); >> - >> - /* >> - * Do not relabel the file if -n was used. >> - */ >> - if (!change || user_only_changed) >> - goto out; >> - >> - /* >> - * Relabel the file to the specified context. >> - */ >> - ret = lsetfilecon(ftsent->fts_accpath, newcon); >> - if (ret) { >> - fprintf(stderr, "%s set context %s->%s failed:'%s'\n", >> - progname, my_file, newcon, strerror(errno)); >> - goto skip; >> - } >> -out: >> - freecon(newcon); >> - return 0; >> -skip: >> - freecon(newcon); >> - return SKIP; >> -err: >> - freecon(newcon); >> - return ERR; >> -} >> - >> -/* >> - * Apply the last matching specification to a file. >> - * This function is called by fts on each file during >> - * the directory traversal. >> - */ >> -static int apply_spec(FTSENT *ftsent) >> -{ >> - if (ftsent->fts_info == FTS_DNR) { >> - fprintf(stderr, "%s: unable to read directory %s\n", >> - progname, ftsent->fts_path); >> - return SKIP; >> - } >> >> - int rc = restore(ftsent); >> - if (rc == ERR) { >> - if (!abort_on_error) >> - return SKIP; >> - } >> - return rc; >> -} >> >> void set_rootpath(const char *arg) >> { >> int len; >> >> - rootpath = strdup(arg); >> - if (NULL == rootpath) { >> - fprintf(stderr, "%s: insufficient memory for rootpath\n", >> - progname); >> + r_opts.rootpath = strdup(arg); >> + if (NULL == r_opts.rootpath) { >> + fprintf(stderr, "%s: insufficient memory for r_opts.rootpath\n", >> + r_opts.progname); >> exit(1); >> } >> >> /* trim trailing /, if present */ >> - len = strlen(rootpath); >> - while (len && ('/' == rootpath[len - 1])) >> - rootpath[--len] = 0; >> - rootpathlen = len; >> + len = strlen(r_opts.rootpath); >> + while (len && ('/' == r_opts.rootpath[len - 1])) >> + r_opts.rootpath[--len] = 0; >> + r_opts.rootpathlen = len; >> } >> >> int canoncon(char **contextp) >> @@ -545,163 +108,6 @@ int canoncon(char **contextp) >> return rc; >> } >> >> -static int symlink_realpath(char *name, char *path) >> -{ >> - char *p = NULL, *file_sep; >> - char *tmp_path = strdupa(name); >> - size_t len = 0; >> - >> - if (!tmp_path) { >> - fprintf(stderr, "strdupa on %s failed: %s\n", name, >> - strerror(errno)); >> - return -1; >> - } >> - file_sep = strrchr(tmp_path, '/'); >> - if (file_sep == tmp_path) { >> - file_sep++; >> - p = strcpy(path, ""); >> - } else if (file_sep) { >> - *file_sep = 0; >> - file_sep++; >> - p = realpath(tmp_path, path); >> - } else { >> - file_sep = tmp_path; >> - p = realpath("./", path); >> - } >> - if (p) >> - len = strlen(p); >> - if (!p || len + strlen(file_sep) + 2 > PATH_MAX) { >> - fprintf(stderr, "symlink_realpath(%s) failed %s\n", name, >> - strerror(errno)); >> - return -1; >> - } >> - p += len; >> - /* ensure trailing slash of directory name */ >> - if (len == 0 || *(p - 1) != '/') { >> - *p = '/'; >> - p++; >> - } >> - strcpy(p, file_sep); >> - return 0; >> -} >> - >> -static int process_one(char *name, int recurse_this_path) >> -{ >> - int rc = 0; >> - const char *namelist[2]; >> - dev_t dev_num = 0; >> - FTS *fts_handle; >> - FTSENT *ftsent; >> - >> - if (!strcmp(name, "/")) >> - mass_relabel = 1; >> - >> - namelist[0] = name; >> - namelist[1] = NULL; >> - fts_handle = fts_open((char **)namelist, fts_flags, NULL); >> - if (fts_handle == NULL) { >> - fprintf(stderr, >> - "%s: error while labeling %s: %s\n", >> - progname, namelist[0], strerror(errno)); >> - goto err; >> - } >> - >> - >> - ftsent = fts_read(fts_handle); >> - if (ftsent != NULL) { >> - /* Keep the inode of the first one. */ >> - dev_num = ftsent->fts_statp->st_dev; >> - } >> - >> - do { >> - /* Skip the post order nodes. */ >> - if (ftsent->fts_info == FTS_DP) >> - continue; >> - /* If the XDEV flag is set and the device is different */ >> - if (ftsent->fts_statp->st_dev != dev_num && >> - FTS_XDEV == (fts_flags & FTS_XDEV)) >> - continue; >> - if (excludeCtr > 0) { >> - if (exclude(ftsent->fts_path)) { >> - fts_set(fts_handle, ftsent, FTS_SKIP); >> - continue; >> - } >> - } >> - int rc = apply_spec(ftsent); >> - if (rc == SKIP) >> - fts_set(fts_handle, ftsent, FTS_SKIP); >> - if (rc == ERR) >> - goto err; >> - if (!recurse_this_path) >> - break; >> - } while ((ftsent = fts_read(fts_handle)) != NULL); >> - >> - if (!strcmp(name, "/")) >> - mass_relabel_errs = 0; >> - >> -out: >> - if (add_assoc) { >> - if (!quiet) >> - filespec_eval(); >> - filespec_destroy(); >> - } >> - if (fts_handle) >> - fts_close(fts_handle); >> - return rc; >> - >> -err: >> - if (!strcmp(name, "/")) >> - mass_relabel_errs = 1; >> - rc = -1; >> - goto out; >> -} >> - >> -static int process_one_realpath(char *name) >> -{ >> - int rc = 0; >> - char *p; >> - struct stat sb; >> - >> - if (!expand_realpath) { >> - return process_one(name, recurse); >> - } else { >> - rc = lstat(name, &sb); >> - if (rc < 0) { >> - fprintf(stderr, "%s: lstat(%s) failed: %s\n", >> - progname, name, strerror(errno)); >> - return -1; >> - } >> - >> - if (S_ISLNK(sb.st_mode)) { >> - char path[PATH_MAX + 1]; >> - >> - rc = symlink_realpath(name, path); >> - if (rc < 0) >> - return rc; >> - rc = process_one(path, 0); >> - if (rc < 0) >> - return rc; >> - >> - p = realpath(name, NULL); >> - if (p) { >> - rc = process_one(p, recurse); >> - free(p); >> - } >> - return rc; >> - } else { >> - p = realpath(name, NULL); >> - if (!p) { >> - fprintf(stderr, "realpath(%s) failed %s\n", name, >> - strerror(errno)); >> - return -1; >> - } >> - rc = process_one(p, recurse); >> - free(p); >> - return rc; >> - } >> - } >> -} >> - >> #ifndef USE_AUDIT >> static void maybe_audit_mass_relabel(void) >> { >> @@ -803,21 +209,32 @@ int main(int argc, char **argv) >> int use_input_file = 0; >> char *buf = NULL; >> size_t buf_len; >> + int recurse; /* Recursive descent. */ >> char *base; >> - struct selinux_opt opts[] = { >> - { SELABEL_OPT_VALIDATE, NULL }, >> - { SELABEL_OPT_PATH, NULL } >> - }; >> + >> + memset(&r_opts, 0, sizeof(r_opts)); >> + >> + /* Initialize variables */ >> + r_opts.progress = 0; >> + r_opts.count = 0; >> + r_opts.debug = 0; >> + r_opts.change = 1; >> + r_opts.verbose = 0; >> + r_opts.logging = 0; >> + r_opts.rootpath = NULL; >> + r_opts.rootpathlen = 0; >> + r_opts.outfile = NULL; >> + r_opts.force = 0; >> + r_opts.hard_links = 1; >> >> - memset(excludeArray, 0, sizeof(excludeArray)); >> altpath = NULL; >> >> - progname = strdup(argv[0]); >> - if (!progname) { >> + r_opts.progname = strdup(argv[0]); >> + if (!r_opts.progname) { >> fprintf(stderr, "%s: Out of memory!\n", argv[0]); >> exit(1); >> } >> - base = basename(progname); >> + base = basename(r_opts.progname); >> >> if (!strcmp(base, SETFILES)) { >> /* >> @@ -831,10 +248,10 @@ int main(int argc, char **argv) >> */ >> iamrestorecon = 0; >> recurse = 1; >> - expand_realpath = 0; >> - abort_on_error = 1; >> - add_assoc = 1; >> - fts_flags = FTS_PHYSICAL | FTS_XDEV; >> + r_opts.expand_realpath = 0; >> + r_opts.abort_on_error = 1; >> + r_opts.add_assoc = 1; >> + r_opts.fts_flags = FTS_PHYSICAL | FTS_XDEV; >> ctx_validate = 1; >> } else { >> /* >> @@ -846,14 +263,14 @@ int main(int argc, char **argv) >> * Follows mounts, >> * Does lazy validation of contexts upon use. >> */ >> - if (strcmp(base, RESTORECON) && !quiet) >> + if (strcmp(base, RESTORECON) && !r_opts.quiet) >> printf("Executed with an unrecognized name (%s), defaulting to %s >> behavior.\n", base, RESTORECON); >> iamrestorecon = 1; >> recurse = 0; >> - expand_realpath = 1; >> - abort_on_error = 0; >> - add_assoc = 0; >> - fts_flags = FTS_PHYSICAL; >> + r_opts.expand_realpath = 1; >> + r_opts.abort_on_error = 0; >> + r_opts.add_assoc = 0; >> + r_opts.fts_flags = FTS_PHYSICAL; >> ctx_validate = 0; >> >> /* restorecon only: silent exit if no SELinux. >> @@ -915,37 +332,37 @@ int main(int argc, char **argv) >> input_filename = optarg; >> break; >> case 'd': >> - debug = 1; >> + r_opts.debug = 1; >> break; >> case 'i': >> ignore_enoent = 1; >> break; >> case 'l': >> - logging = 1; >> + r_opts.logging = 1; >> break; >> case 'F': >> - force = 1; >> + r_opts.force = 1; >> break; >> case 'n': >> - change = 0; >> + r_opts.change = 0; >> break; >> case 'o': >> if (strcmp(optarg, "-") == 0) { >> - outfile = stdout; >> + r_opts.outfile = stdout; >> break; >> } >> >> - outfile = fopen(optarg, "w"); >> - if (!outfile) { >> + r_opts.outfile = fopen(optarg, "w"); >> + if (!r_opts.outfile) { >> fprintf(stderr, "Error opening %s: %s\n", >> optarg, strerror(errno)); >> >> usage(argv[0]); >> } >> - __fsetlocking(outfile, FSETLOCKING_BYCALLER); >> + __fsetlocking(r_opts.outfile, FSETLOCKING_BYCALLER); >> break; >> case 'q': >> - quiet = 1; >> + r_opts.quiet = 1; >> break; >> case 'R': >> case 'r': >> @@ -954,11 +371,11 @@ int main(int argc, char **argv) >> break; >> } >> if (optind + 1 >= argc) { >> - fprintf(stderr, "usage: %s -r rootpath\n", >> + fprintf(stderr, "usage: %s -r r_opts.rootpath\n", >> argv[0]); >> exit(1); >> } >> - if (NULL != rootpath) { >> + if (NULL != r_opts.rootpath) { >> fprintf(stderr, >> "%s: only one -r can be specified\n", >> argv[0]); >> @@ -969,23 +386,23 @@ int main(int argc, char **argv) >> case 's': >> use_input_file = 1; >> input_filename = "-"; >> - add_assoc = 0; >> + r_opts.add_assoc = 0; >> break; >> case 'v': >> - if (progress) { >> + if (r_opts.progress) { >> fprintf(stderr, >> "Progress and Verbose mutually exclusive\n"); >> exit(1); >> } >> - verbose++; >> + r_opts.verbose++; >> break; >> case 'p': >> - if (verbose) { >> + if (r_opts.verbose) { >> fprintf(stderr, >> "Progress and Verbose mutually exclusive\n"); >> usage(argv[0]); >> } >> - progress = 1; >> + r_opts.progress = 1; >> break; >> case 'W': >> warn_no_match = 1; >> @@ -1033,18 +450,13 @@ int main(int argc, char **argv) >> } >> >> /* Load the file contexts configuration and check it. */ >> - opts[0].value = (ctx_validate ? (char*)1 : NULL); >> - opts[1].value = altpath; >> - >> - hnd = selabel_open(SELABEL_CTX_FILE, opts, 2); >> - if (!hnd) { >> - perror(altpath); >> - exit(1); >> - } >> + r_opts.selabel_opt_validate = (ctx_validate ? (char *)1 : NULL); >> + r_opts.selabel_opt_path = altpath; >> >> if (nerr) >> exit(1); >> >> + restore_init(&r_opts); >> if (use_input_file) { >> FILE *f = stdin; >> ssize_t len; >> @@ -1061,31 +473,34 @@ int main(int argc, char **argv) >> delim = (null_terminated != 0) ? '\0' : '\n'; >> while ((len = getdelim(&buf, &buf_len, delim, f)) > 0) { >> buf[len - 1] = 0; >> - errors |= process_one_realpath(buf); >> + if (!strcmp(buf, "/")) >> + mass_relabel = 1; >> + errors |= process_one_realpath(buf, recurse) < 0; >> } >> if (strcmp(input_filename, "-") != 0) >> fclose(f); >> } else { >> for (i = optind; i < argc; i++) { >> - errors |= process_one_realpath(argv[i]); >> + if (!strcmp(argv[i], "/")) >> + mass_relabel = 1; >> + errors |= process_one_realpath(argv[i], recurse) < 0; >> } >> } >> - >> + >> + if (mass_relabel) >> + mass_relabel_errs = errors; >> maybe_audit_mass_relabel(); >> >> if (warn_no_match) >> - selabel_stats(hnd); >> + selabel_stats(r_opts.hnd); >> >> - selabel_close(hnd); >> + selabel_close(r_opts.hnd); >> + restore_finish(); >> >> - if (outfile) >> - fclose(outfile); >> - >> - for (i = 0; i < excludeCtr; i++) { >> - free(excludeArray[i].directory); >> - } >> + if (r_opts.outfile) >> + fclose(r_opts.outfile); >> >> - if (progress && count >= STAR_COUNT) >> + if (r_opts.progress && r_opts.count >= STAR_COUNT) >> printf("\n"); >> exit(errors); >> } > -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.