Re: [PATCH 1/2] policycoreutils: share setfiles restore function with restorecond

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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()?
> 2) Why the new check at the beginning of match()?
> 
> 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);
>  }
Chad, once you have patch 1 in I will send you the restorecond patch, with all the missing files.

--
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.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux