1bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines/*
2bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines * The majority of this code is from Android's
3bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines * external/libselinux/src/android.c and upstream
4f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * selinux/policycoreutils/setfiles/restore.c
5bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines *
6bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines * See selinux_restorecon(3) for details.
7bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines */
8bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
9bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <unistd.h>
10bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <string.h>
11bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <stdio.h>
12bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <stdlib.h>
13bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <stdbool.h>
14bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <ctype.h>
15bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <errno.h>
16bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <fcntl.h>
17bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <fts.h>
18bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <limits.h>
19f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#include <stdint.h>
20bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <sys/types.h>
21bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <sys/stat.h>
22bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <sys/xattr.h>
23bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <sys/vfs.h>
24f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#include <sys/statvfs.h>
25f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#include <sys/utsname.h>
26bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <linux/magic.h>
27bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <libgen.h>
28f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#include <syslog.h>
29f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#include <assert.h>
30f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
31bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <selinux/selinux.h>
32bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <selinux/context.h>
33bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <selinux/label.h>
34bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include <selinux/restorecon.h>
35bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
36bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include "callbacks.h"
37bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#include "selinux_internal.h"
38bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
39bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#define RESTORECON_LAST "security.restorecon_last"
40bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
41bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#define SYS_PATH "/sys"
42bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines#define SYS_PREFIX SYS_PATH "/"
43bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
44454768f56d7a941657d800e303994bca086b7546Richard Haines#define STAR_COUNT 1024
45f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
46bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic struct selabel_handle *fc_sehandle = NULL;
47bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic unsigned char *fc_digest = NULL;
48bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic size_t fc_digest_len = 0;
49f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic char *rootpath = NULL;
50f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int rootpathlen;
51f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
52f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines/* Information on excluded fs and directories. */
53f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstruct edir {
54f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	char *directory;
55f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	size_t size;
56f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* True if excluded by selinux_restorecon_set_exclude_list(3). */
57f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	bool caller_excluded;
58f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines};
59f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines#define CALLER_EXCLUDED true
60f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic bool ignore_mounts;
61f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int exclude_non_seclabel_mounts(void);
62f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int exclude_count = 0;
63f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic struct edir *exclude_lst = NULL;
64f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic uint64_t fc_count = 0;	/* Number of files processed so far */
65f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic uint64_t efile_count;	/* Estimated total number of files */
66f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
672d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines/* Store information on directories with xattr's. */
682d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainesstruct dir_xattr *dir_xattr_list;
692d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainesstatic struct dir_xattr *dir_xattr_last;
702d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
71d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/* restorecon_flags for passing to restorecon_sb() */
72d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesstruct rest_flags {
73d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool nochange;
74d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool verbose;
75d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool progress;
76454768f56d7a941657d800e303994bca086b7546Richard Haines	bool mass_relabel;
77d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool set_specctx;
78d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool add_assoc;
79d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool ignore_digest;
80d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool recurse;
81d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool userealpath;
82d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	bool set_xdev;
83f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	bool abort_on_error;
84f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	bool syslog_changes;
85f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	bool log_matches;
86f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	bool ignore_noent;
871cd972fc81757d6157afa192da99473243dfce8bStephen Smalley	bool warnonnomatch;
88d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines};
89d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
90bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic void restorecon_init(void)
91bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
92bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	struct selabel_handle *sehandle = NULL;
93bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
94bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (!fc_sehandle) {
95bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		sehandle = selinux_restorecon_default_handle();
96bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		selinux_restorecon_set_sehandle(sehandle);
97bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
98f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
99f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	efile_count = 0;
100f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!ignore_mounts)
101f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		efile_count = exclude_non_seclabel_mounts();
102bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
103bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
104bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic pthread_once_t fc_once = PTHREAD_ONCE_INIT;
105bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
106f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines/*
107f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * Manage excluded directories:
108f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *  remove_exclude() - This removes any conflicting entries as there could be
109f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                     a case where a non-seclabel fs is mounted on /foo and
110f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                     then a seclabel fs is mounted on top of it.
111f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                     However if an entry has been added via
112f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                     selinux_restorecon_set_exclude_list(3) do not remove.
113f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *
114f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *  add_exclude()    - Add a directory/fs to be excluded from labeling. If it
115f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                     has already been added, then ignore.
116f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *
117f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *  check_excluded() - Check if directory/fs is to be excluded when relabeling.
118f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *
119f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *  file_system_count() - Calculates the the number of files to be processed.
120f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                        The count is only used if SELINUX_RESTORECON_PROGRESS
121f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                        is set and a mass relabel is requested.
122f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *
123f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *  exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what
124f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  non-seclabel mounts to exclude from
125f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  relabeling. restorecon_init() will not
126f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  call this function if the
127f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  SELINUX_RESTORECON_IGNORE_MOUNTS
128f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  flag is set.
129f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  Setting SELINUX_RESTORECON_IGNORE_MOUNTS
130f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  is useful where there is a non-seclabel fs
131f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  mounted on /foo and then a seclabel fs is
132f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *                                  mounted on a directory below this.
133f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines */
134f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic void remove_exclude(const char *directory)
135f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines{
136f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int i;
137f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
138f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	for (i = 0; i < exclude_count; i++) {
139f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (strcmp(directory, exclude_lst[i].directory) == 0 &&
140f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					!exclude_lst[i].caller_excluded) {
141f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			free(exclude_lst[i].directory);
142f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (i != exclude_count - 1)
143f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				exclude_lst[i] = exclude_lst[exclude_count - 1];
144f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			exclude_count--;
145f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			return;
146f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
147f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
148f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines}
149f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
150f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int add_exclude(const char *directory, bool who)
151f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines{
152f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	struct edir *tmp_list, *current;
153f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	size_t len = 0;
154f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int i;
155f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
156f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* Check if already present. */
157f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	for (i = 0; i < exclude_count; i++) {
158f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (strcmp(directory, exclude_lst[i].directory) == 0)
159f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			return 0;
160f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
161f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
162f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (directory == NULL || directory[0] != '/') {
163f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		selinux_log(SELINUX_ERROR,
164f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			    "Full path required for exclude: %s.\n",
165f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			    directory);
166f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		errno = EINVAL;
167f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		return -1;
168f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
169f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
170f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	tmp_list = realloc(exclude_lst,
171f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			   sizeof(struct edir) * (exclude_count + 1));
172f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!tmp_list)
173f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		goto oom;
174f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
175f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	exclude_lst = tmp_list;
176f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
177f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	len = strlen(directory);
178f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	while (len > 1 && directory[len - 1] == '/')
179f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		len--;
180f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
181f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	current = (exclude_lst + exclude_count);
182f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
183f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	current->directory = strndup(directory, len);
184f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!current->directory)
185f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		goto oom;
186f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
187f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	current->size = len;
188f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	current->caller_excluded = who;
189f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	exclude_count++;
190f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	return 0;
191f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
192f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesoom:
193f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
194f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	return -1;
195f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines}
196bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
197bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic int check_excluded(const char *file)
198bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
199bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	int i;
200bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
201f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	for (i = 0; i < exclude_count; i++) {
202f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (strncmp(file, exclude_lst[i].directory,
203f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		    exclude_lst[i].size) == 0) {
204f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (file[exclude_lst[i].size] == 0 ||
205f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					 file[exclude_lst[i].size] == '/')
206bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				return 1;
207f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
208bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
209bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	return 0;
210bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
211bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
212f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int file_system_count(char *name)
213f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines{
214f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	struct statvfs statvfs_buf;
215f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int nfile = 0;
216f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
217f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	memset(&statvfs_buf, 0, sizeof(statvfs_buf));
218f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!statvfs(name, &statvfs_buf))
219f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		nfile = statvfs_buf.f_files - statvfs_buf.f_ffree;
220f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
221f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	return nfile;
222f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines}
223f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
224d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
225f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * This is called once when selinux_restorecon() is first called.
226f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * Searches /proc/mounts for all file systems that do not support extended
227f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * attributes and adds them to the exclude directory table.  File systems
228f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * that support security labels have the seclabel option, return
229f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * approximate total file count.
230f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines */
231f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesstatic int exclude_non_seclabel_mounts(void)
232f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines{
233f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	struct utsname uts;
234f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	FILE *fp;
235f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	size_t len;
236f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	ssize_t num;
237f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int index = 0, found = 0, nfile = 0;
238f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	char *mount_info[4];
239f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	char *buf = NULL, *item;
240f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
241f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* Check to see if the kernel supports seclabel */
242f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0)
243f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		return 0;
244f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
24564afa1aff1cd610d2493f780e2a44b551f668b84Nick Kralevich	fp = fopen("/proc/mounts", "re");
246f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!fp)
247f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		return 0;
248f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
249f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	while ((num = getline(&buf, &len, fp)) != -1) {
250f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		found = 0;
251f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		index = 0;
252f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		item = strtok(buf, " ");
253f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		while (item != NULL) {
254f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			mount_info[index] = item;
255f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			index++;
2569d3091a6d3f1c4e40b67716aca740031699150a2Nicolas Iooss			if (index == 4)
2579d3091a6d3f1c4e40b67716aca740031699150a2Nicolas Iooss				break;
258f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			item = strtok(NULL, " ");
259f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
2609d3091a6d3f1c4e40b67716aca740031699150a2Nicolas Iooss		if (index < 4) {
261f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			selinux_log(SELINUX_ERROR,
262f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    "/proc/mounts record \"%s\" has incorrect format.\n",
263f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    buf);
264f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			continue;
265f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
266f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
267f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		/* Remove pre-existing entry */
268f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		remove_exclude(mount_info[1]);
269f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
270f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		item = strtok(mount_info[3], ",");
271f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		while (item != NULL) {
272f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (strcmp(item, "seclabel") == 0) {
273f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				found = 1;
274f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				nfile += file_system_count(mount_info[1]);
275f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				break;
276f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			}
277f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			item = strtok(NULL, ",");
278f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
279f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
280f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		/* Exclude mount points without the seclabel option */
281f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (!found) {
282f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (add_exclude(mount_info[1], !CALLER_EXCLUDED) &&
283f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			    errno == ENOMEM)
284f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				assert(0);
285f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
286f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
287f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
288f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	free(buf);
289f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	fclose(fp);
290f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* return estimated #Files + 5% for directories and hard links */
291f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	return nfile * 1.05;
292f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines}
293f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
2942d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */
2952d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainesstatic int add_xattr_entry(const char *directory, bool delete_nonmatch,
2962d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			   bool delete_all)
2972d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines{
2982d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	char *sha1_buf = NULL;
2992d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	unsigned char *xattr_value = NULL;
3002d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	ssize_t xattr_size;
3012d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	size_t i;
3022d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	int rc, digest_result;
3032d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	struct dir_xattr *new_entry;
3042d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3052d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!directory) {
3062d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		errno = EINVAL;
3072d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return -1;
3082d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
3092d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3102d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	xattr_value = malloc(fc_digest_len);
3112d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!xattr_value)
3122d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		goto oom;
3132d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3142d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	xattr_size = getxattr(directory, RESTORECON_LAST, xattr_value,
3152d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			      fc_digest_len);
3162d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (xattr_size < 0) {
3172d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		free(xattr_value);
3182d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return 1;
3192d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
3202d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3212d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	/* Convert entry to a hex encoded string. */
3222d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	sha1_buf = malloc(xattr_size * 2 + 1);
3232d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!sha1_buf) {
3242d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		free(xattr_value);
3252d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		goto oom;
3262d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
3272d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3282d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	for (i = 0; i < (size_t)xattr_size; i++)
3292d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		sprintf((&sha1_buf[i * 2]), "%02x", xattr_value[i]);
3302d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3312d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	rc = memcmp(fc_digest, xattr_value, fc_digest_len);
3322d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	digest_result = rc ? NOMATCH : MATCH;
3332d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3342d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if ((delete_nonmatch && rc != 0) || delete_all) {
3352d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH;
3362d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		rc = removexattr(directory, RESTORECON_LAST);
3372d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		if (rc) {
3382d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			selinux_log(SELINUX_ERROR,
3392d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				  "Error: %s removing xattr \"%s\" from: %s\n",
3402d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				  strerror(errno), RESTORECON_LAST, directory);
3412d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			digest_result = ERROR;
3422d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		}
3432d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
3442d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	free(xattr_value);
3452d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3462d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	/* Now add entries to link list. */
3472d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	new_entry = malloc(sizeof(struct dir_xattr));
3482d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!new_entry)
3492d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		goto oom;
3502d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	new_entry->next = NULL;
3512d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3522d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	new_entry->directory = strdup(directory);
3532d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!new_entry->directory)
3542d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		goto oom;
3552d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3562d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	new_entry->digest = strdup(sha1_buf);
3572d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!new_entry->digest)
3582d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		goto oom;
3592d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3602d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	new_entry->result = digest_result;
3612d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3622d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!dir_xattr_list) {
3632d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		dir_xattr_list = new_entry;
3642d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		dir_xattr_last = new_entry;
3652d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	} else {
3662d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		dir_xattr_last->next = new_entry;
3672d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		dir_xattr_last = new_entry;
3682d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
3692d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3702d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	free(sha1_buf);
3712d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	return 0;
3722d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
3732d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainesoom:
3742d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
3752d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	return -1;
3762d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines}
3772d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
378f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines/*
379f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * Support filespec services filespec_add(), filespec_eval() and
380f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * filespec_destroy().
381f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines *
382f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * selinux_restorecon(3) uses filespec services when the
383f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between
384f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * an inode and a specification.
385d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
386d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
387d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
388d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * The hash table of associations, hashed by inode number. Chaining is used
389d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * for collisions, with elements ordered by inode number in each bucket.
390d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * Each hash bucket has a dummy header.
391d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
392d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines#define HASH_BITS 16
393d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines#define HASH_BUCKETS (1 << HASH_BITS)
394d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines#define HASH_MASK (HASH_BUCKETS-1)
395d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
396d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
397d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * An association between an inode and a context.
398d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
399d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainestypedef struct file_spec {
400d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	ino_t ino;		/* inode number */
401d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	char *con;		/* matched context */
402d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	char *file;		/* full pathname */
403d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	struct file_spec *next;	/* next association in hash bucket chain */
404d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines} file_spec_t;
405d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
406d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesstatic file_spec_t *fl_head;
407d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
408d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
409d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * Try to add an association between an inode and a context. If there is a
410d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * different context that matched the inode, then use the first context
411d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * that matched.
412d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
413d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesstatic int filespec_add(ino_t ino, const char *con, const char *file)
414d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines{
415d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	file_spec_t *prevfl, *fl;
416d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	int h, ret;
417d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	struct stat64 sb;
418d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
419d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl_head) {
420d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
421d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (!fl_head)
422d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			goto oom;
423d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
424d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
425d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
426d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
427d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
428d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	     prevfl = fl, fl = fl->next) {
429d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (ino == fl->ino) {
430d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			ret = lstat64(fl->file, &sb);
431d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			if (ret < 0 || sb.st_ino != ino) {
432d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				freecon(fl->con);
433d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				free(fl->file);
434d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				fl->file = strdup(file);
435d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				if (!fl->file)
436d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines					goto oom;
437d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				fl->con = strdup(con);
438d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				if (!fl->con)
439d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines					goto oom;
440d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				return 1;
441d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			}
442d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
443d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			if (strcmp(fl->con, con) == 0)
444d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				return 1;
445d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
446d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			selinux_log(SELINUX_ERROR,
447d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				"conflicting specifications for %s and %s, using %s.\n",
448d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				file, fl->file, fl->con);
449d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			free(fl->file);
450d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			fl->file = strdup(file);
451d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			if (!fl->file)
452d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				goto oom;
453d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			return 1;
454d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		}
455d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
456d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (ino > fl->ino)
457d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			break;
458d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
459d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
460d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl = malloc(sizeof(file_spec_t));
461d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl)
462d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		goto oom;
463d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl->ino = ino;
464d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl->con = strdup(con);
465d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl->con)
466d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		goto oom_freefl;
467d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl->file = strdup(file);
468d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl->file)
469d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		goto oom_freefl;
470d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl->next = prevfl->next;
471d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	prevfl->next = fl;
472d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	return 0;
473d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
474d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesoom_freefl:
475d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	free(fl);
476d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesoom:
477d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
478d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	return -1;
479d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines}
480d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
481d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
482d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * Evaluate the association hash table distribution.
483d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
484b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalley#ifdef DEBUG
485d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesstatic void filespec_eval(void)
486d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines{
487d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	file_spec_t *fl;
488d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	int h, used, nel, len, longest;
489d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
490d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl_head)
491d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		return;
492d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
493d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	used = 0;
494d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	longest = 0;
495d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	nel = 0;
496d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	for (h = 0; h < HASH_BUCKETS; h++) {
497d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		len = 0;
498d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		for (fl = fl_head[h].next; fl; fl = fl->next)
499d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			len++;
500d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (len)
501d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			used++;
502d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (len > longest)
503d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			longest = len;
504d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		nel += len;
505d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
506d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
507d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	selinux_log(SELINUX_INFO,
508d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		     "filespec hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
509d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		     nel, used, HASH_BUCKETS, longest);
510d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines}
511b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalley#else
512b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalleystatic void filespec_eval(void)
513b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalley{
514b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalley}
515b88c4a4707c942028d7d0035bba9057fa031e83bStephen Smalley#endif
516d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
517d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
518d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * Destroy the association hash table.
519d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
520d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Hainesstatic void filespec_destroy(void)
521d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines{
522d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	file_spec_t *fl, *tmp;
523d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	int h;
524d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
525d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!fl_head)
526d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		return;
527d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
528d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	for (h = 0; h < HASH_BUCKETS; h++) {
529d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		fl = fl_head[h].next;
530d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		while (fl) {
531d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			tmp = fl;
532d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			fl = fl->next;
533d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			freecon(tmp->con);
534d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			free(tmp->file);
535d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			free(tmp);
536d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		}
537d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		fl_head[h].next = NULL;
538d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
539d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	free(fl_head);
540d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	fl_head = NULL;
541d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines}
542d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
543d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
544d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if
545d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * the type components differ, updating newtypecon if so.
546d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
547bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic int compare_types(char *curcon, char *newcon, char **newtypecon)
548bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
549bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	int types_differ = 0;
550bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	context_t cona;
551bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	context_t conb;
552bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	int rc = 0;
553bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
554bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	cona = context_new(curcon);
555bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (!cona) {
556bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		rc = -1;
557bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		goto out;
558bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
559bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	conb = context_new(newcon);
560bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (!conb) {
561bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		context_free(cona);
562bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		rc = -1;
563bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		goto out;
564bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
565bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
566bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	types_differ = strcmp(context_type_get(cona), context_type_get(conb));
567bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (types_differ) {
568bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		rc |= context_user_set(conb, context_user_get(cona));
569bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		rc |= context_role_set(conb, context_role_get(cona));
570bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		rc |= context_range_set(conb, context_range_get(cona));
571bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (!rc) {
572bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			*newtypecon = strdup(context_str(conb));
573bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (!*newtypecon) {
574bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				rc = -1;
575bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto err;
576bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			}
577bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
578bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
579bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
580bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haineserr:
581bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	context_free(cona);
582bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	context_free(conb);
583bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesout:
584bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	return rc;
585bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
586bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
587bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstatic int restorecon_sb(const char *pathname, const struct stat *sb,
588d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			    struct rest_flags *flags)
589bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
590bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	char *newcon = NULL;
591bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	char *curcon = NULL;
592bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	char *newtypecon = NULL;
593f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int rc;
594bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	bool updated = false;
595f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	const char *lookup_path = pathname;
596f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	float pc;
597f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
598f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (rootpath) {
599f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (strncmp(rootpath, lookup_path, rootpathlen) != 0) {
600f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			selinux_log(SELINUX_ERROR,
601f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    "%s is not located in alt_rootpath %s\n",
602f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    lookup_path, rootpath);
603f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			return -1;
604f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
605f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		lookup_path += rootpathlen;
606f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
607f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
608f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (rootpath != NULL && lookup_path[0] == '\0')
609f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		/* this is actually the root dir of the alt root. */
610f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
611f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines						    sb->st_mode);
612f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	else
613f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path,
614f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines						    sb->st_mode);
615f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
616f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (rc < 0) {
6171cd972fc81757d6157afa192da99473243dfce8bStephen Smalley		if (errno == ENOENT && flags->warnonnomatch)
618f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			selinux_log(SELINUX_INFO,
619f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    "Warning no default label for %s\n",
620f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    lookup_path);
621bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
622bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		return 0; /* no match, but not an error */
623f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
624f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
625f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (flags->progress) {
626f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		fc_count++;
627f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (fc_count % STAR_COUNT == 0) {
628454768f56d7a941657d800e303994bca086b7546Richard Haines			if (flags->mass_relabel && efile_count > 0) {
629f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				pc = (fc_count < efile_count) ? (100.0 *
630f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					     fc_count / efile_count) : 100;
631f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				fprintf(stdout, "\r%-.1f%%", (double)pc);
632f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			} else {
633454768f56d7a941657d800e303994bca086b7546Richard Haines				fprintf(stdout, "\r%luk", fc_count / STAR_COUNT);
634f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			}
635454768f56d7a941657d800e303994bca086b7546Richard Haines			fflush(stdout);
636f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
637f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
638bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
639d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (flags->add_assoc) {
640d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		rc = filespec_add(sb->st_ino, newcon, pathname);
641d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
642d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (rc < 0) {
643d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			selinux_log(SELINUX_ERROR,
644d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines				    "filespec_add error: %s\n", pathname);
645d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			freecon(newcon);
646d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			return -1;
647d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		}
648d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
649d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (rc > 0) {
650d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			/* Already an association and it took precedence. */
651d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			freecon(newcon);
652d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			return 0;
653d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		}
654d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
655d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
656f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (flags->log_matches)
657f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		selinux_log(SELINUX_INFO, "%s matched by %s\n",
658f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			    pathname, newcon);
659f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
660bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (lgetfilecon_raw(pathname, &curcon) < 0) {
661bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (errno != ENODATA)
662bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			goto err;
663bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
664bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		curcon = NULL;
665bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
666bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
667a9094fae2c7bcd14af7263f9bb7fbacff5bc761cNicolas Iooss	if (curcon == NULL || strcmp(curcon, newcon) != 0) {
668d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (!flags->set_specctx && curcon &&
669bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    (is_context_customizable(curcon) > 0)) {
670d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			if (flags->verbose) {
671bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				selinux_log(SELINUX_INFO,
672bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				 "%s not reset as customized by admin to %s\n",
673bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines							    pathname, curcon);
674bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto out;
675bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			}
676bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
677bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
678d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (!flags->set_specctx && curcon) {
679bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			/* If types different then update newcon. */
680bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			rc = compare_types(curcon, newcon, &newtypecon);
681bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (rc)
682bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto err;
683bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
684bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (newtypecon) {
685bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				freecon(newcon);
686bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				newcon = newtypecon;
687bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			} else {
688bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto out;
689bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			}
690bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
691bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
692d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (!flags->nochange) {
693bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (lsetfilecon(pathname, newcon) < 0)
694bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto err;
695bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			updated = true;
696bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
697bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
698d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (flags->verbose)
699bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_INFO,
700bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    "%s %s from %s to %s\n",
701bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    updated ? "Relabeled" : "Would relabel",
702bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    pathname, curcon, newcon);
703f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
704f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (flags->syslog_changes && !flags->nochange) {
705f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (curcon)
706f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				syslog(LOG_INFO,
707f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					    "relabeling %s from %s to %s\n",
708f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					    pathname, curcon, newcon);
709f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			else
710f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				syslog(LOG_INFO, "labeling %s to %s\n",
711f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines					    pathname, newcon);
712f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
713bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
714bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
715bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesout:
716bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	rc = 0;
717bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesout1:
718bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	freecon(curcon);
719bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	freecon(newcon);
720bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	return rc;
721bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haineserr:
722bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	selinux_log(SELINUX_ERROR,
723bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    "Could not set context for %s:  %s\n",
724bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    pathname, strerror(errno));
725bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	rc = -1;
726bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	goto out1;
727bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
728bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
729bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines/*
730bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines * Public API
731bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines */
732bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
733bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines/* selinux_restorecon(3) - Main function that is responsible for labeling */
734bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesint selinux_restorecon(const char *pathname_orig,
735bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    unsigned int restorecon_flags)
736bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
737d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	struct rest_flags flags;
738d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
739d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.ignore_digest = (restorecon_flags &
740bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
741d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.nochange = (restorecon_flags &
742bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
743d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.verbose = (restorecon_flags &
744bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_VERBOSE) ? true : false;
745d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.progress = (restorecon_flags &
746bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_PROGRESS) ? true : false;
747454768f56d7a941657d800e303994bca086b7546Richard Haines	flags.mass_relabel = (restorecon_flags &
748454768f56d7a941657d800e303994bca086b7546Richard Haines		    SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
749d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.recurse = (restorecon_flags &
750bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_RECURSE) ? true : false;
751d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.set_specctx = (restorecon_flags &
752bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
753d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.userealpath = (restorecon_flags &
754bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		   SELINUX_RESTORECON_REALPATH) ? true : false;
755d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.set_xdev = (restorecon_flags &
756bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		   SELINUX_RESTORECON_XDEV) ? true : false;
757d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	flags.add_assoc = (restorecon_flags &
758d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
759f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	flags.abort_on_error = (restorecon_flags &
760f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
761f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	flags.syslog_changes = (restorecon_flags &
762f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
763f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	flags.log_matches = (restorecon_flags &
764f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
765f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	flags.ignore_noent = (restorecon_flags &
766f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
7671cd972fc81757d6157afa192da99473243dfce8bStephen Smalley	flags.warnonnomatch = true;
768f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	ignore_mounts = (restorecon_flags &
769f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
770d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
771bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	bool issys;
772bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
773bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines					* FALSE = don't use xattr */
774bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	struct stat sb;
775bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	struct statfs sfsb;
776bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	FTS *fts;
777bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	FTSENT *ftsent;
778bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
779f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	char *paths[2] = { NULL, NULL };
780f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int fts_flags, error, sverrno;
781bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	char *xattr_value = NULL;
782bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	ssize_t size;
783f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	dev_t dev_num = 0;
784bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
785d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (flags.verbose && flags.progress)
786d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		flags.verbose = false;
787bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
788bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	__selinux_once(fc_once, restorecon_init);
789bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
790bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (!fc_sehandle)
791bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		return -1;
792bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
793bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (fc_digest_len) {
794bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		xattr_value = malloc(fc_digest_len);
795bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (!xattr_value)
796bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			return -1;
797bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
798bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
799bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	/*
800bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	 * Convert passed-in pathname to canonical pathname by resolving
801bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	 * realpath of containing dir, then appending last component name.
802bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	 */
803d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (flags.userealpath) {
804aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley		char *basename_cpy = strdup(pathname_orig);
805aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley		if (!basename_cpy)
806aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			goto realpatherr;
807aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley		pathbname = basename(basename_cpy);
808bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") ||
809bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines					    !strcmp(pathbname, "..")) {
810bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			pathname = realpath(pathname_orig, NULL);
811aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			if (!pathname) {
812aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley				free(basename_cpy);
813bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto realpatherr;
814aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			}
815bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		} else {
816aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			char *dirname_cpy = strdup(pathname_orig);
817aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			if (!dirname_cpy) {
818aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley				free(basename_cpy);
819aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley				goto realpatherr;
820aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			}
821aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			pathdname = dirname(dirname_cpy);
822bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			pathdnamer = realpath(pathdname, NULL);
823aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			free(dirname_cpy);
824aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			if (!pathdnamer) {
825aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley				free(basename_cpy);
826bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto realpatherr;
827aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			}
828bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (!strcmp(pathdnamer, "/"))
829bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				error = asprintf(&pathname, "/%s", pathbname);
830bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			else
831bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				error = asprintf(&pathname, "%s/%s",
832bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						    pathdnamer, pathbname);
833aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			if (error < 0) {
834aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley				free(basename_cpy);
835bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				goto oom;
836aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley			}
837bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
838aa0c824bb2eeb8960ba02133faade72c837ea951Stephen Smalley		free(basename_cpy);
839bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	} else {
840bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		pathname = strdup(pathname_orig);
841bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (!pathname)
842bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			goto oom;
843bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
844bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
845bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	paths[0] = pathname;
846bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	issys = (!strcmp(pathname, SYS_PATH) ||
847bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			    !strncmp(pathname, SYS_PREFIX,
848bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			    sizeof(SYS_PREFIX) - 1)) ? true : false;
849bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
850bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (lstat(pathname, &sb) < 0) {
851f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (flags.ignore_noent && errno == ENOENT) {
852f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			free(pathdnamer);
853f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			free(pathname);
854f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			return 0;
855f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		} else {
856f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			selinux_log(SELINUX_ERROR,
857f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    "lstat(%s) failed: %s\n",
858f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    pathname, strerror(errno));
859f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			error = -1;
860f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			goto cleanup;
861f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
862bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
863bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
864bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	/* Ignore restoreconlast if not a directory */
865bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if ((sb.st_mode & S_IFDIR) != S_IFDIR)
866bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		setrestoreconlast = false;
867bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
868d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (!flags.recurse) {
869f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (check_excluded(pathname)) {
870f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			error = 0;
871f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			goto cleanup;
872f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
873f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
874d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		error = restorecon_sb(pathname, &sb, &flags);
875bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		goto cleanup;
876bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
877bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
878bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	/* Ignore restoreconlast on /sys */
879bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (issys)
880bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		setrestoreconlast = false;
881bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
882bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	/* Ignore restoreconlast on in-memory filesystems */
883bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (statfs(pathname, &sfsb) == 0) {
884bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
885bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			setrestoreconlast = false;
886bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
887bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
888bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (setrestoreconlast) {
889bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		size = getxattr(pathname, RESTORECON_LAST, xattr_value,
890bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines							    fc_digest_len);
891bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
8928647a6c6214be2e6b87526c5755480b5bb3452fdNicolas Iooss		if (!flags.ignore_digest && (size_t)size == fc_digest_len &&
893bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			    memcmp(fc_digest, xattr_value, fc_digest_len)
894bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines								    == 0) {
895bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_INFO,
896bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			    "Skipping restorecon as matching digest on: %s\n",
897bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    pathname);
898bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			error = 0;
899bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			goto cleanup;
900bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
901bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
902bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
903d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (flags.set_xdev)
904bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
905bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	else
906bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
907bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
908bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	fts = fts_open(paths, fts_flags, NULL);
909f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!fts)
910f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		goto fts_err;
911f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
912f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	ftsent = fts_read(fts);
913f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!ftsent)
914f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		goto fts_err;
915f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
916f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/*
917f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * Keep the inode of the first device. This is because the FTS_XDEV
918f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * flag tells fts not to descend into directories with different
919f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * device numbers, but fts will still give back the actual directory.
920f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * By saving the device number of the directory that was passed to
921f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * selinux_restorecon() and then skipping all actions on any
922f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * directories with a different device number when the FTS_XDEV flag
923f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
924f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 */
925f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	dev_num = ftsent->fts_statp->st_dev;
926bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
927bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	error = 0;
928f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	do {
929f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		/* If the FTS_XDEV flag is set and the device is different */
930f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num)
931f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			continue;
932f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
933bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		switch (ftsent->fts_info) {
934bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_DC:
935bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_ERROR,
936bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    "Directory cycle on %s.\n",
937bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    ftsent->fts_path);
938bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			errno = ELOOP;
939bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			error = -1;
940bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			goto out;
941bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_DP:
942bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			continue;
943bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_DNR:
944bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_ERROR,
945bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    "Could not read %s: %s.\n",
946bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    ftsent->fts_path,
947bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						  strerror(ftsent->fts_errno));
948bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			fts_set(fts, ftsent, FTS_SKIP);
949bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			continue;
950bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_NS:
951bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_ERROR,
952bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    "Could not stat %s: %s.\n",
953bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    ftsent->fts_path,
954bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						  strerror(ftsent->fts_errno));
955bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			fts_set(fts, ftsent, FTS_SKIP);
956bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			continue;
957bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_ERR:
958bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_ERROR,
959bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    "Error on %s: %s.\n",
960bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				    ftsent->fts_path,
961bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						  strerror(ftsent->fts_errno));
962bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			fts_set(fts, ftsent, FTS_SKIP);
963bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			continue;
964bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		case FTS_D:
965bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			if (issys && !selabel_partial_match(fc_sehandle,
966bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines					    ftsent->fts_path)) {
967bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				fts_set(fts, ftsent, FTS_SKIP);
968bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				continue;
969bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			}
970f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
971f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (check_excluded(ftsent->fts_path)) {
972f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				fts_set(fts, ftsent, FTS_SKIP);
973f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				continue;
974f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			}
975bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			/* fall through */
976bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		default:
977bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			error |= restorecon_sb(ftsent->fts_path,
978d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines					       ftsent->fts_statp, &flags);
9791cd972fc81757d6157afa192da99473243dfce8bStephen Smalley			if (flags.warnonnomatch)
9801cd972fc81757d6157afa192da99473243dfce8bStephen Smalley				flags.warnonnomatch = false;
981f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			if (error && flags.abort_on_error)
982f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				goto out;
983bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			break;
984bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		}
985f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	} while ((ftsent = fts_read(fts)) != NULL);
986bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
987bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	/* Labeling successful. Mark the top level directory as completed. */
988f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (setrestoreconlast && !flags.nochange && !error && fc_digest) {
989bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		error = setxattr(pathname, RESTORECON_LAST, fc_digest,
990bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						    fc_digest_len, 0);
991d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (!error && flags.verbose)
992bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			selinux_log(SELINUX_INFO,
993bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				   "Updated digest for: %s\n", pathname);
994bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
995bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
996bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesout:
997454768f56d7a941657d800e303994bca086b7546Richard Haines	if (flags.progress && flags.mass_relabel)
998454768f56d7a941657d800e303994bca086b7546Richard Haines		fprintf(stdout, "\r%s 100.0%%\n", pathname);
999f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1000bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	sverrno = errno;
1001bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	(void) fts_close(fts);
1002bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	errno = sverrno;
1003bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainescleanup:
1004d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	if (flags.add_assoc) {
1005d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		if (flags.verbose)
1006d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines			filespec_eval();
1007d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines		filespec_destroy();
1008d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines	}
1009bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	free(pathdnamer);
1010bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	free(pathname);
1011bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	free(xattr_value);
1012bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	return error;
1013d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
1014bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesoom:
1015bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	sverrno = errno;
1016bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
1017bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	errno = sverrno;
1018bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	error = -1;
1019bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	goto cleanup;
1020d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines
1021bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesrealpatherr:
1022bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	sverrno = errno;
1023bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	selinux_log(SELINUX_ERROR,
1024bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    "SELinux: Could not get canonical path for %s restorecon: %s.\n",
1025bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		    pathname_orig, strerror(errno));
1026bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	errno = sverrno;
1027bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	error = -1;
1028bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	goto cleanup;
1029f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1030f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesfts_err:
1031f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	selinux_log(SELINUX_ERROR,
1032f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		    "fts error while labeling %s: %s\n",
1033f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		    paths[0], strerror(errno));
1034f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	error = -1;
1035f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	goto cleanup;
1036bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
1037bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1038bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
1039bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesvoid selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
1040bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
1041f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	char **specfiles;
1042f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	size_t num_specfiles;
1043bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1044bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	fc_sehandle = (struct selabel_handle *) hndl;
1045bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1046f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/*
1047f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 * Read digest if requested in selabel_open(3) and set global params.
1048f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	 */
1049f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
1050bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines				   &specfiles, &num_specfiles) < 0) {
1051bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		fc_digest = NULL;
1052bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		fc_digest_len = 0;
1053bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
1054bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
1055bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1056d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
1057d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * selinux_restorecon_default_handle(3) is called to set the global restorecon
1058d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines * handle by a process if the default params are required.
1059d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
1060bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesstruct selabel_handle *selinux_restorecon_default_handle(void)
1061bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
1062bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	struct selabel_handle *sehandle;
1063bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1064bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	struct selinux_opt fc_opts[] = {
1065bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		{ SELABEL_OPT_DIGEST, (char *)1 }
1066bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	};
1067bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1068bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
1069bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1070bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	if (!sehandle) {
1071bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		selinux_log(SELINUX_ERROR,
1072bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines			    "Error obtaining file context handle: %s\n",
1073bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines						    strerror(errno));
1074bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines		return NULL;
1075bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	}
1076bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1077bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines	return sehandle;
1078bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
1079bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines
1080d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines/*
1081f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * selinux_restorecon_set_exclude_list(3) is called to add additional entries
1082f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines * to be excluded from labeling checks.
1083d4a46eec3fb271ccb442b876617361f5885d8d7aRichard Haines */
1084bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Hainesvoid selinux_restorecon_set_exclude_list(const char **exclude_list)
1085bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines{
1086f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int i;
1087f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	struct stat sb;
1088f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1089f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	for (i = 0; exclude_list[i]; i++) {
1090f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) {
1091f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			selinux_log(SELINUX_ERROR,
1092f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    "lstat error on exclude path \"%s\", %s - ignoring.\n",
1093f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines				    exclude_list[i], strerror(errno));
1094f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			break;
1095f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		}
1096f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		if (add_exclude(exclude_list[i], CALLER_EXCLUDED) &&
1097f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		    errno == ENOMEM)
1098f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines			assert(0);
1099f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
1100f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines}
1101f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1102f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
1103f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Hainesint selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
1104f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines{
1105f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	int len;
1106f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1107f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* This should be NULL on first use */
1108f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (rootpath)
1109f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		free(rootpath);
1110f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1111f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	rootpath = strdup(alt_rootpath);
1112f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	if (!rootpath) {
1113f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
1114f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		return -1;
1115f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	}
1116f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1117f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	/* trim trailing /, if present */
1118f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	len = strlen(rootpath);
1119f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	while (len && (rootpath[len - 1] == '/'))
1120f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines		rootpath[--len] = '\0';
1121f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	rootpathlen = len;
1122f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines
1123f2e77865e144ab2e1313aa78d99b969f8f48695eRichard Haines	return 0;
1124bdd4e6d2b1bf8b7897be87056326f0dca18c1b94Richard Haines}
11252d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11262d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines/* selinux_restorecon_xattr(3) - Find RESTORECON_LAST entries. */
11272d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainesint selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
11282d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines					    struct dir_xattr ***xattr_list)
11292d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines{
11302d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	bool recurse = (xattr_flags &
11312d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	    SELINUX_RESTORECON_XATTR_RECURSE) ? true : false;
11322d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	bool delete_nonmatch = (xattr_flags &
11332d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	    SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false;
11342d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	bool delete_all = (xattr_flags &
11352d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	    SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false;
11362d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	ignore_mounts = (xattr_flags &
11372d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	   SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false;
11382d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11392d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	int rc, fts_flags;
11402d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	struct stat sb;
11412d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	struct statfs sfsb;
11422d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	struct dir_xattr *current, *next;
11432d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	FTS *fts;
11442d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	FTSENT *ftsent;
11452d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	char *paths[2] = { NULL, NULL };
11462d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11472d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	__selinux_once(fc_once, restorecon_init);
11482d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11492d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!fc_sehandle || !fc_digest_len)
11502d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return -1;
11512d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11522d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (lstat(pathname, &sb) < 0) {
11532d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		if (errno == ENOENT)
11542d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			return 0;
11552d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11562d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		selinux_log(SELINUX_ERROR,
11572d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			    "lstat(%s) failed: %s\n",
11582d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			    pathname, strerror(errno));
11592d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return -1;
11602d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
11612d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11622d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!recurse) {
11632d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		if (statfs(pathname, &sfsb) == 0) {
11642d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			if (sfsb.f_type == RAMFS_MAGIC ||
11652d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			    sfsb.f_type == TMPFS_MAGIC)
11662d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				return 0;
11672d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		}
11682d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11692d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		if (check_excluded(pathname))
11702d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			return 0;
11712d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11722d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		rc = add_xattr_entry(pathname, delete_nonmatch, delete_all);
11732d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11742d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		if (!rc && dir_xattr_list)
11752d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			*xattr_list = &dir_xattr_list;
11762d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		else if (rc == -1)
11772d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			return rc;
11782d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11792d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return 0;
11802d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
11812d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11822d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	paths[0] = (char *)pathname;
11832d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
11842d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11852d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	fts = fts_open(paths, fts_flags, NULL);
11862d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (!fts) {
11872d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		selinux_log(SELINUX_ERROR,
11882d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			    "fts error on %s: %s\n",
11892d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			    paths[0], strerror(errno));
11902d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		return -1;
11912d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
11922d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
11932d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	while ((ftsent = fts_read(fts)) != NULL) {
11942d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		switch (ftsent->fts_info) {
11952d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		case FTS_DP:
11962d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			continue;
11972d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		case FTS_D:
11982d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			if (statfs(ftsent->fts_path, &sfsb) == 0) {
11992d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				if (sfsb.f_type == RAMFS_MAGIC ||
12002d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				    sfsb.f_type == TMPFS_MAGIC)
12012d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines					continue;
12022d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			}
12032d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			if (check_excluded(ftsent->fts_path)) {
12042d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				fts_set(fts, ftsent, FTS_SKIP);
12052d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				continue;
12062d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			}
12072d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
12082d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			rc = add_xattr_entry(ftsent->fts_path,
12092d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines					     delete_nonmatch, delete_all);
12102d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			if (rc == 1)
12112d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				continue;
12122d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			else if (rc == -1)
12132d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines				goto cleanup;
12142d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			break;
12152d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		default:
12162d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			break;
12172d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		}
12182d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
12192d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
12202d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (dir_xattr_list)
12212d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		*xattr_list = &dir_xattr_list;
12222d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
12232d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	(void) fts_close(fts);
12242d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	return 0;
12252d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
12262d814ff4c7798a26d29e1dd091e75043f1628561Richard Hainescleanup:
12272d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	rc = errno;
12282d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	(void) fts_close(fts);
12292d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	errno = rc;
12302d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines
12312d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	if (dir_xattr_list) {
12322d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		/* Free any used memory */
12332d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		current = dir_xattr_list;
12342d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		while (current) {
12352d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			next = current->next;
12362d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			free(current->directory);
12372d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			free(current->digest);
12382d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			free(current);
12392d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines			current = next;
12402d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines		}
12412d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	}
12422d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines	return -1;
12432d814ff4c7798a26d29e1dd091e75043f1628561Richard Haines}
1244