xref: /aosp_15_r20/external/selinux/libselinux/src/selinux_restorecon.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1*2d543d20SAndroid Build Coastguard Worker /*
2*2d543d20SAndroid Build Coastguard Worker  * The majority of this code is from Android's
3*2d543d20SAndroid Build Coastguard Worker  * external/libselinux/src/android.c and upstream
4*2d543d20SAndroid Build Coastguard Worker  * selinux/policycoreutils/setfiles/restore.c
5*2d543d20SAndroid Build Coastguard Worker  *
6*2d543d20SAndroid Build Coastguard Worker  * See selinux_restorecon(3) for details.
7*2d543d20SAndroid Build Coastguard Worker  */
8*2d543d20SAndroid Build Coastguard Worker 
9*2d543d20SAndroid Build Coastguard Worker #include <unistd.h>
10*2d543d20SAndroid Build Coastguard Worker #include <string.h>
11*2d543d20SAndroid Build Coastguard Worker #include <stdio.h>
12*2d543d20SAndroid Build Coastguard Worker #include <stdlib.h>
13*2d543d20SAndroid Build Coastguard Worker #include <stdbool.h>
14*2d543d20SAndroid Build Coastguard Worker #include <ctype.h>
15*2d543d20SAndroid Build Coastguard Worker #include <errno.h>
16*2d543d20SAndroid Build Coastguard Worker #include <fcntl.h>
17*2d543d20SAndroid Build Coastguard Worker #include <fts.h>
18*2d543d20SAndroid Build Coastguard Worker #include <inttypes.h>
19*2d543d20SAndroid Build Coastguard Worker #include <limits.h>
20*2d543d20SAndroid Build Coastguard Worker #include <stdint.h>
21*2d543d20SAndroid Build Coastguard Worker #include <sys/types.h>
22*2d543d20SAndroid Build Coastguard Worker #include <sys/stat.h>
23*2d543d20SAndroid Build Coastguard Worker #include <sys/xattr.h>
24*2d543d20SAndroid Build Coastguard Worker #include <sys/vfs.h>
25*2d543d20SAndroid Build Coastguard Worker #include <sys/statvfs.h>
26*2d543d20SAndroid Build Coastguard Worker #include <sys/utsname.h>
27*2d543d20SAndroid Build Coastguard Worker #include <linux/magic.h>
28*2d543d20SAndroid Build Coastguard Worker #include <libgen.h>
29*2d543d20SAndroid Build Coastguard Worker #include <syslog.h>
30*2d543d20SAndroid Build Coastguard Worker #include <assert.h>
31*2d543d20SAndroid Build Coastguard Worker 
32*2d543d20SAndroid Build Coastguard Worker #include <selinux/selinux.h>
33*2d543d20SAndroid Build Coastguard Worker #include <selinux/context.h>
34*2d543d20SAndroid Build Coastguard Worker #include <selinux/label.h>
35*2d543d20SAndroid Build Coastguard Worker #include <selinux/restorecon.h>
36*2d543d20SAndroid Build Coastguard Worker 
37*2d543d20SAndroid Build Coastguard Worker #include "callbacks.h"
38*2d543d20SAndroid Build Coastguard Worker #include "selinux_internal.h"
39*2d543d20SAndroid Build Coastguard Worker #include "label_file.h"
40*2d543d20SAndroid Build Coastguard Worker #include "sha1.h"
41*2d543d20SAndroid Build Coastguard Worker 
42*2d543d20SAndroid Build Coastguard Worker #define STAR_COUNT 1024
43*2d543d20SAndroid Build Coastguard Worker 
44*2d543d20SAndroid Build Coastguard Worker static struct selabel_handle *fc_sehandle = NULL;
45*2d543d20SAndroid Build Coastguard Worker static bool selabel_no_digest;
46*2d543d20SAndroid Build Coastguard Worker static char *rootpath = NULL;
47*2d543d20SAndroid Build Coastguard Worker static size_t rootpathlen;
48*2d543d20SAndroid Build Coastguard Worker 
49*2d543d20SAndroid Build Coastguard Worker /* Information on excluded fs and directories. */
50*2d543d20SAndroid Build Coastguard Worker struct edir {
51*2d543d20SAndroid Build Coastguard Worker 	char *directory;
52*2d543d20SAndroid Build Coastguard Worker 	size_t size;
53*2d543d20SAndroid Build Coastguard Worker 	/* True if excluded by selinux_restorecon_set_exclude_list(3). */
54*2d543d20SAndroid Build Coastguard Worker 	bool caller_excluded;
55*2d543d20SAndroid Build Coastguard Worker };
56*2d543d20SAndroid Build Coastguard Worker #define CALLER_EXCLUDED true
57*2d543d20SAndroid Build Coastguard Worker static bool ignore_mounts;
58*2d543d20SAndroid Build Coastguard Worker static uint64_t exclude_non_seclabel_mounts(void);
59*2d543d20SAndroid Build Coastguard Worker static int exclude_count = 0;
60*2d543d20SAndroid Build Coastguard Worker static struct edir *exclude_lst = NULL;
61*2d543d20SAndroid Build Coastguard Worker static uint64_t fc_count = 0;	/* Number of files processed so far */
62*2d543d20SAndroid Build Coastguard Worker static uint64_t efile_count;	/* Estimated total number of files */
63*2d543d20SAndroid Build Coastguard Worker static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
64*2d543d20SAndroid Build Coastguard Worker 
65*2d543d20SAndroid Build Coastguard Worker /* Store information on directories with xattr's. */
66*2d543d20SAndroid Build Coastguard Worker static struct dir_xattr *dir_xattr_list;
67*2d543d20SAndroid Build Coastguard Worker static struct dir_xattr *dir_xattr_last;
68*2d543d20SAndroid Build Coastguard Worker 
69*2d543d20SAndroid Build Coastguard Worker /* Number of errors ignored during the file tree walk. */
70*2d543d20SAndroid Build Coastguard Worker static long unsigned skipped_errors;
71*2d543d20SAndroid Build Coastguard Worker 
72*2d543d20SAndroid Build Coastguard Worker /* restorecon_flags for passing to restorecon_sb() */
73*2d543d20SAndroid Build Coastguard Worker struct rest_flags {
74*2d543d20SAndroid Build Coastguard Worker 	bool nochange;
75*2d543d20SAndroid Build Coastguard Worker 	bool verbose;
76*2d543d20SAndroid Build Coastguard Worker 	bool progress;
77*2d543d20SAndroid Build Coastguard Worker 	bool mass_relabel;
78*2d543d20SAndroid Build Coastguard Worker 	bool set_specctx;
79*2d543d20SAndroid Build Coastguard Worker 	bool add_assoc;
80*2d543d20SAndroid Build Coastguard Worker 	bool recurse;
81*2d543d20SAndroid Build Coastguard Worker 	bool userealpath;
82*2d543d20SAndroid Build Coastguard Worker 	bool set_xdev;
83*2d543d20SAndroid Build Coastguard Worker 	bool abort_on_error;
84*2d543d20SAndroid Build Coastguard Worker 	bool syslog_changes;
85*2d543d20SAndroid Build Coastguard Worker 	bool log_matches;
86*2d543d20SAndroid Build Coastguard Worker 	bool ignore_noent;
87*2d543d20SAndroid Build Coastguard Worker 	bool warnonnomatch;
88*2d543d20SAndroid Build Coastguard Worker 	bool conflicterror;
89*2d543d20SAndroid Build Coastguard Worker 	bool count_errors;
90*2d543d20SAndroid Build Coastguard Worker };
91*2d543d20SAndroid Build Coastguard Worker 
restorecon_init(void)92*2d543d20SAndroid Build Coastguard Worker static void restorecon_init(void)
93*2d543d20SAndroid Build Coastguard Worker {
94*2d543d20SAndroid Build Coastguard Worker 	struct selabel_handle *sehandle = NULL;
95*2d543d20SAndroid Build Coastguard Worker 
96*2d543d20SAndroid Build Coastguard Worker 	if (!fc_sehandle) {
97*2d543d20SAndroid Build Coastguard Worker 		sehandle = selinux_restorecon_default_handle();
98*2d543d20SAndroid Build Coastguard Worker 		selinux_restorecon_set_sehandle(sehandle);
99*2d543d20SAndroid Build Coastguard Worker 	}
100*2d543d20SAndroid Build Coastguard Worker 
101*2d543d20SAndroid Build Coastguard Worker 	efile_count = 0;
102*2d543d20SAndroid Build Coastguard Worker 	if (!ignore_mounts)
103*2d543d20SAndroid Build Coastguard Worker 		efile_count = exclude_non_seclabel_mounts();
104*2d543d20SAndroid Build Coastguard Worker }
105*2d543d20SAndroid Build Coastguard Worker 
106*2d543d20SAndroid Build Coastguard Worker static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
107*2d543d20SAndroid Build Coastguard Worker 
108*2d543d20SAndroid Build Coastguard Worker /*
109*2d543d20SAndroid Build Coastguard Worker  * Manage excluded directories:
110*2d543d20SAndroid Build Coastguard Worker  *  remove_exclude() - This removes any conflicting entries as there could be
111*2d543d20SAndroid Build Coastguard Worker  *                     a case where a non-seclabel fs is mounted on /foo and
112*2d543d20SAndroid Build Coastguard Worker  *                     then a seclabel fs is mounted on top of it.
113*2d543d20SAndroid Build Coastguard Worker  *                     However if an entry has been added via
114*2d543d20SAndroid Build Coastguard Worker  *                     selinux_restorecon_set_exclude_list(3) do not remove.
115*2d543d20SAndroid Build Coastguard Worker  *
116*2d543d20SAndroid Build Coastguard Worker  *  add_exclude()    - Add a directory/fs to be excluded from labeling. If it
117*2d543d20SAndroid Build Coastguard Worker  *                     has already been added, then ignore.
118*2d543d20SAndroid Build Coastguard Worker  *
119*2d543d20SAndroid Build Coastguard Worker  *  check_excluded() - Check if directory/fs is to be excluded when relabeling.
120*2d543d20SAndroid Build Coastguard Worker  *
121*2d543d20SAndroid Build Coastguard Worker  *  file_system_count() - Calculates the number of files to be processed.
122*2d543d20SAndroid Build Coastguard Worker  *                        The count is only used if SELINUX_RESTORECON_PROGRESS
123*2d543d20SAndroid Build Coastguard Worker  *                        is set and a mass relabel is requested.
124*2d543d20SAndroid Build Coastguard Worker  *
125*2d543d20SAndroid Build Coastguard Worker  *  exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what
126*2d543d20SAndroid Build Coastguard Worker  *                                  non-seclabel mounts to exclude from
127*2d543d20SAndroid Build Coastguard Worker  *                                  relabeling. restorecon_init() will not
128*2d543d20SAndroid Build Coastguard Worker  *                                  call this function if the
129*2d543d20SAndroid Build Coastguard Worker  *                                  SELINUX_RESTORECON_IGNORE_MOUNTS
130*2d543d20SAndroid Build Coastguard Worker  *                                  flag is set.
131*2d543d20SAndroid Build Coastguard Worker  *                                  Setting SELINUX_RESTORECON_IGNORE_MOUNTS
132*2d543d20SAndroid Build Coastguard Worker  *                                  is useful where there is a non-seclabel fs
133*2d543d20SAndroid Build Coastguard Worker  *                                  mounted on /foo and then a seclabel fs is
134*2d543d20SAndroid Build Coastguard Worker  *                                  mounted on a directory below this.
135*2d543d20SAndroid Build Coastguard Worker  */
remove_exclude(const char * directory)136*2d543d20SAndroid Build Coastguard Worker static void remove_exclude(const char *directory)
137*2d543d20SAndroid Build Coastguard Worker {
138*2d543d20SAndroid Build Coastguard Worker 	int i;
139*2d543d20SAndroid Build Coastguard Worker 
140*2d543d20SAndroid Build Coastguard Worker 	for (i = 0; i < exclude_count; i++) {
141*2d543d20SAndroid Build Coastguard Worker 		if (strcmp(directory, exclude_lst[i].directory) == 0 &&
142*2d543d20SAndroid Build Coastguard Worker 					!exclude_lst[i].caller_excluded) {
143*2d543d20SAndroid Build Coastguard Worker 			free(exclude_lst[i].directory);
144*2d543d20SAndroid Build Coastguard Worker 			if (i != exclude_count - 1)
145*2d543d20SAndroid Build Coastguard Worker 				exclude_lst[i] = exclude_lst[exclude_count - 1];
146*2d543d20SAndroid Build Coastguard Worker 			exclude_count--;
147*2d543d20SAndroid Build Coastguard Worker 			return;
148*2d543d20SAndroid Build Coastguard Worker 		}
149*2d543d20SAndroid Build Coastguard Worker 	}
150*2d543d20SAndroid Build Coastguard Worker }
151*2d543d20SAndroid Build Coastguard Worker 
add_exclude(const char * directory,bool who)152*2d543d20SAndroid Build Coastguard Worker static int add_exclude(const char *directory, bool who)
153*2d543d20SAndroid Build Coastguard Worker {
154*2d543d20SAndroid Build Coastguard Worker 	struct edir *tmp_list, *current;
155*2d543d20SAndroid Build Coastguard Worker 	size_t len = 0;
156*2d543d20SAndroid Build Coastguard Worker 	int i;
157*2d543d20SAndroid Build Coastguard Worker 
158*2d543d20SAndroid Build Coastguard Worker 	/* Check if already present. */
159*2d543d20SAndroid Build Coastguard Worker 	for (i = 0; i < exclude_count; i++) {
160*2d543d20SAndroid Build Coastguard Worker 		if (strcmp(directory, exclude_lst[i].directory) == 0)
161*2d543d20SAndroid Build Coastguard Worker 			return 0;
162*2d543d20SAndroid Build Coastguard Worker 	}
163*2d543d20SAndroid Build Coastguard Worker 
164*2d543d20SAndroid Build Coastguard Worker 	if (directory == NULL || directory[0] != '/') {
165*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR,
166*2d543d20SAndroid Build Coastguard Worker 			    "Full path required for exclude: %s.\n",
167*2d543d20SAndroid Build Coastguard Worker 			    directory);
168*2d543d20SAndroid Build Coastguard Worker 		errno = EINVAL;
169*2d543d20SAndroid Build Coastguard Worker 		return -1;
170*2d543d20SAndroid Build Coastguard Worker 	}
171*2d543d20SAndroid Build Coastguard Worker 
172*2d543d20SAndroid Build Coastguard Worker 	if (exclude_count >= INT_MAX - 1) {
173*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR, "Too many directory excludes: %d.\n", exclude_count);
174*2d543d20SAndroid Build Coastguard Worker 		errno = EOVERFLOW;
175*2d543d20SAndroid Build Coastguard Worker 		return -1;
176*2d543d20SAndroid Build Coastguard Worker 	}
177*2d543d20SAndroid Build Coastguard Worker 
178*2d543d20SAndroid Build Coastguard Worker 	tmp_list = reallocarray(exclude_lst, exclude_count + 1, sizeof(struct edir));
179*2d543d20SAndroid Build Coastguard Worker 	if (!tmp_list)
180*2d543d20SAndroid Build Coastguard Worker 		goto oom;
181*2d543d20SAndroid Build Coastguard Worker 
182*2d543d20SAndroid Build Coastguard Worker 	exclude_lst = tmp_list;
183*2d543d20SAndroid Build Coastguard Worker 
184*2d543d20SAndroid Build Coastguard Worker 	len = strlen(directory);
185*2d543d20SAndroid Build Coastguard Worker 	while (len > 1 && directory[len - 1] == '/')
186*2d543d20SAndroid Build Coastguard Worker 		len--;
187*2d543d20SAndroid Build Coastguard Worker 
188*2d543d20SAndroid Build Coastguard Worker 	current = (exclude_lst + exclude_count);
189*2d543d20SAndroid Build Coastguard Worker 
190*2d543d20SAndroid Build Coastguard Worker 	current->directory = strndup(directory, len);
191*2d543d20SAndroid Build Coastguard Worker 	if (!current->directory)
192*2d543d20SAndroid Build Coastguard Worker 		goto oom;
193*2d543d20SAndroid Build Coastguard Worker 
194*2d543d20SAndroid Build Coastguard Worker 	current->size = len;
195*2d543d20SAndroid Build Coastguard Worker 	current->caller_excluded = who;
196*2d543d20SAndroid Build Coastguard Worker 	exclude_count++;
197*2d543d20SAndroid Build Coastguard Worker 	return 0;
198*2d543d20SAndroid Build Coastguard Worker 
199*2d543d20SAndroid Build Coastguard Worker oom:
200*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
201*2d543d20SAndroid Build Coastguard Worker 	return -1;
202*2d543d20SAndroid Build Coastguard Worker }
203*2d543d20SAndroid Build Coastguard Worker 
check_excluded(const char * file)204*2d543d20SAndroid Build Coastguard Worker static int check_excluded(const char *file)
205*2d543d20SAndroid Build Coastguard Worker {
206*2d543d20SAndroid Build Coastguard Worker 	int i;
207*2d543d20SAndroid Build Coastguard Worker 
208*2d543d20SAndroid Build Coastguard Worker 	for (i = 0; i < exclude_count; i++) {
209*2d543d20SAndroid Build Coastguard Worker 		if (strncmp(file, exclude_lst[i].directory,
210*2d543d20SAndroid Build Coastguard Worker 		    exclude_lst[i].size) == 0) {
211*2d543d20SAndroid Build Coastguard Worker 			if (file[exclude_lst[i].size] == 0 ||
212*2d543d20SAndroid Build Coastguard Worker 					 file[exclude_lst[i].size] == '/')
213*2d543d20SAndroid Build Coastguard Worker 				return 1;
214*2d543d20SAndroid Build Coastguard Worker 		}
215*2d543d20SAndroid Build Coastguard Worker 	}
216*2d543d20SAndroid Build Coastguard Worker 	return 0;
217*2d543d20SAndroid Build Coastguard Worker }
218*2d543d20SAndroid Build Coastguard Worker 
file_system_count(const char * name)219*2d543d20SAndroid Build Coastguard Worker static uint64_t file_system_count(const char *name)
220*2d543d20SAndroid Build Coastguard Worker {
221*2d543d20SAndroid Build Coastguard Worker 	struct statvfs statvfs_buf;
222*2d543d20SAndroid Build Coastguard Worker 	uint64_t nfile = 0;
223*2d543d20SAndroid Build Coastguard Worker 
224*2d543d20SAndroid Build Coastguard Worker 	memset(&statvfs_buf, 0, sizeof(statvfs_buf));
225*2d543d20SAndroid Build Coastguard Worker 	if (!statvfs(name, &statvfs_buf))
226*2d543d20SAndroid Build Coastguard Worker 		nfile = statvfs_buf.f_files - statvfs_buf.f_ffree;
227*2d543d20SAndroid Build Coastguard Worker 
228*2d543d20SAndroid Build Coastguard Worker 	return nfile;
229*2d543d20SAndroid Build Coastguard Worker }
230*2d543d20SAndroid Build Coastguard Worker 
231*2d543d20SAndroid Build Coastguard Worker /*
232*2d543d20SAndroid Build Coastguard Worker  * This is called once when selinux_restorecon() is first called.
233*2d543d20SAndroid Build Coastguard Worker  * Searches /proc/mounts for all file systems that do not support extended
234*2d543d20SAndroid Build Coastguard Worker  * attributes and adds them to the exclude directory table.  File systems
235*2d543d20SAndroid Build Coastguard Worker  * that support security labels have the seclabel option, return
236*2d543d20SAndroid Build Coastguard Worker  * approximate total file count.
237*2d543d20SAndroid Build Coastguard Worker  */
exclude_non_seclabel_mounts(void)238*2d543d20SAndroid Build Coastguard Worker static uint64_t exclude_non_seclabel_mounts(void)
239*2d543d20SAndroid Build Coastguard Worker {
240*2d543d20SAndroid Build Coastguard Worker 	struct utsname uts;
241*2d543d20SAndroid Build Coastguard Worker 	FILE *fp;
242*2d543d20SAndroid Build Coastguard Worker 	size_t len;
243*2d543d20SAndroid Build Coastguard Worker 	int index = 0, found = 0;
244*2d543d20SAndroid Build Coastguard Worker 	uint64_t nfile = 0;
245*2d543d20SAndroid Build Coastguard Worker 	char *mount_info[4];
246*2d543d20SAndroid Build Coastguard Worker 	char *buf = NULL, *item, *saveptr;
247*2d543d20SAndroid Build Coastguard Worker 
248*2d543d20SAndroid Build Coastguard Worker 	/* Check to see if the kernel supports seclabel */
249*2d543d20SAndroid Build Coastguard Worker 	if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0)
250*2d543d20SAndroid Build Coastguard Worker 		return 0;
251*2d543d20SAndroid Build Coastguard Worker 	if (is_selinux_enabled() <= 0)
252*2d543d20SAndroid Build Coastguard Worker 		return 0;
253*2d543d20SAndroid Build Coastguard Worker 
254*2d543d20SAndroid Build Coastguard Worker 	fp = fopen("/proc/mounts", "re");
255*2d543d20SAndroid Build Coastguard Worker 	if (!fp)
256*2d543d20SAndroid Build Coastguard Worker 		return 0;
257*2d543d20SAndroid Build Coastguard Worker 
258*2d543d20SAndroid Build Coastguard Worker 	while (getline(&buf, &len, fp) != -1) {
259*2d543d20SAndroid Build Coastguard Worker 		found = 0;
260*2d543d20SAndroid Build Coastguard Worker 		index = 0;
261*2d543d20SAndroid Build Coastguard Worker 		saveptr = NULL;
262*2d543d20SAndroid Build Coastguard Worker 		item = strtok_r(buf, " ", &saveptr);
263*2d543d20SAndroid Build Coastguard Worker 		while (item != NULL) {
264*2d543d20SAndroid Build Coastguard Worker 			mount_info[index] = item;
265*2d543d20SAndroid Build Coastguard Worker 			index++;
266*2d543d20SAndroid Build Coastguard Worker 			if (index == 4)
267*2d543d20SAndroid Build Coastguard Worker 				break;
268*2d543d20SAndroid Build Coastguard Worker 			item = strtok_r(NULL, " ", &saveptr);
269*2d543d20SAndroid Build Coastguard Worker 		}
270*2d543d20SAndroid Build Coastguard Worker 		if (index < 4) {
271*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
272*2d543d20SAndroid Build Coastguard Worker 				    "/proc/mounts record \"%s\" has incorrect format.\n",
273*2d543d20SAndroid Build Coastguard Worker 				    buf);
274*2d543d20SAndroid Build Coastguard Worker 			continue;
275*2d543d20SAndroid Build Coastguard Worker 		}
276*2d543d20SAndroid Build Coastguard Worker 
277*2d543d20SAndroid Build Coastguard Worker 		/* Remove pre-existing entry */
278*2d543d20SAndroid Build Coastguard Worker 		remove_exclude(mount_info[1]);
279*2d543d20SAndroid Build Coastguard Worker 
280*2d543d20SAndroid Build Coastguard Worker 		saveptr = NULL;
281*2d543d20SAndroid Build Coastguard Worker 		item = strtok_r(mount_info[3], ",", &saveptr);
282*2d543d20SAndroid Build Coastguard Worker 		while (item != NULL) {
283*2d543d20SAndroid Build Coastguard Worker 			if (strcmp(item, "seclabel") == 0) {
284*2d543d20SAndroid Build Coastguard Worker 				found = 1;
285*2d543d20SAndroid Build Coastguard Worker 				nfile += file_system_count(mount_info[1]);
286*2d543d20SAndroid Build Coastguard Worker 				break;
287*2d543d20SAndroid Build Coastguard Worker 			}
288*2d543d20SAndroid Build Coastguard Worker 			item = strtok_r(NULL, ",", &saveptr);
289*2d543d20SAndroid Build Coastguard Worker 		}
290*2d543d20SAndroid Build Coastguard Worker 
291*2d543d20SAndroid Build Coastguard Worker 		/* Exclude mount points without the seclabel option */
292*2d543d20SAndroid Build Coastguard Worker 		if (!found) {
293*2d543d20SAndroid Build Coastguard Worker 			if (add_exclude(mount_info[1], !CALLER_EXCLUDED) &&
294*2d543d20SAndroid Build Coastguard Worker 			    errno == ENOMEM)
295*2d543d20SAndroid Build Coastguard Worker 				assert(0);
296*2d543d20SAndroid Build Coastguard Worker 		}
297*2d543d20SAndroid Build Coastguard Worker 	}
298*2d543d20SAndroid Build Coastguard Worker 
299*2d543d20SAndroid Build Coastguard Worker 	free(buf);
300*2d543d20SAndroid Build Coastguard Worker 	fclose(fp);
301*2d543d20SAndroid Build Coastguard Worker 	/* return estimated #Files + 5% for directories and hard links */
302*2d543d20SAndroid Build Coastguard Worker 	return nfile * 1.05;
303*2d543d20SAndroid Build Coastguard Worker }
304*2d543d20SAndroid Build Coastguard Worker 
305*2d543d20SAndroid Build Coastguard Worker /* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */
add_xattr_entry(const char * directory,bool delete_nonmatch,bool delete_all)306*2d543d20SAndroid Build Coastguard Worker static int add_xattr_entry(const char *directory, bool delete_nonmatch,
307*2d543d20SAndroid Build Coastguard Worker 			   bool delete_all)
308*2d543d20SAndroid Build Coastguard Worker {
309*2d543d20SAndroid Build Coastguard Worker 	char *sha1_buf = NULL;
310*2d543d20SAndroid Build Coastguard Worker 	size_t i, digest_len = 0;
311*2d543d20SAndroid Build Coastguard Worker 	int rc;
312*2d543d20SAndroid Build Coastguard Worker 	enum digest_result digest_result;
313*2d543d20SAndroid Build Coastguard Worker 	bool match;
314*2d543d20SAndroid Build Coastguard Worker 	struct dir_xattr *new_entry;
315*2d543d20SAndroid Build Coastguard Worker 	uint8_t *xattr_digest = NULL;
316*2d543d20SAndroid Build Coastguard Worker 	uint8_t *calculated_digest = NULL;
317*2d543d20SAndroid Build Coastguard Worker 
318*2d543d20SAndroid Build Coastguard Worker 	if (!directory) {
319*2d543d20SAndroid Build Coastguard Worker 		errno = EINVAL;
320*2d543d20SAndroid Build Coastguard Worker 		return -1;
321*2d543d20SAndroid Build Coastguard Worker 	}
322*2d543d20SAndroid Build Coastguard Worker 
323*2d543d20SAndroid Build Coastguard Worker 	match = selabel_get_digests_all_partial_matches(fc_sehandle, directory,
324*2d543d20SAndroid Build Coastguard Worker 								&calculated_digest, &xattr_digest,
325*2d543d20SAndroid Build Coastguard Worker 								&digest_len);
326*2d543d20SAndroid Build Coastguard Worker 
327*2d543d20SAndroid Build Coastguard Worker 	if (!xattr_digest || !digest_len) {
328*2d543d20SAndroid Build Coastguard Worker 		free(calculated_digest);
329*2d543d20SAndroid Build Coastguard Worker 		return 1;
330*2d543d20SAndroid Build Coastguard Worker 	}
331*2d543d20SAndroid Build Coastguard Worker 
332*2d543d20SAndroid Build Coastguard Worker 	/* Convert entry to a hex encoded string. */
333*2d543d20SAndroid Build Coastguard Worker 	sha1_buf = malloc(digest_len * 2 + 1);
334*2d543d20SAndroid Build Coastguard Worker 	if (!sha1_buf) {
335*2d543d20SAndroid Build Coastguard Worker 		free(xattr_digest);
336*2d543d20SAndroid Build Coastguard Worker 		free(calculated_digest);
337*2d543d20SAndroid Build Coastguard Worker 		goto oom;
338*2d543d20SAndroid Build Coastguard Worker 	}
339*2d543d20SAndroid Build Coastguard Worker 
340*2d543d20SAndroid Build Coastguard Worker 	for (i = 0; i < digest_len; i++)
341*2d543d20SAndroid Build Coastguard Worker 		sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]);
342*2d543d20SAndroid Build Coastguard Worker 
343*2d543d20SAndroid Build Coastguard Worker 	digest_result = match ? MATCH : NOMATCH;
344*2d543d20SAndroid Build Coastguard Worker 
345*2d543d20SAndroid Build Coastguard Worker 	if ((delete_nonmatch && !match) || delete_all) {
346*2d543d20SAndroid Build Coastguard Worker 		digest_result = match ? DELETED_MATCH : DELETED_NOMATCH;
347*2d543d20SAndroid Build Coastguard Worker 		rc = removexattr(directory, RESTORECON_PARTIAL_MATCH_DIGEST);
348*2d543d20SAndroid Build Coastguard Worker 		if (rc) {
349*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
350*2d543d20SAndroid Build Coastguard Worker 				  "Error: %m removing xattr \"%s\" from: %s\n",
351*2d543d20SAndroid Build Coastguard Worker 				  RESTORECON_PARTIAL_MATCH_DIGEST, directory);
352*2d543d20SAndroid Build Coastguard Worker 			digest_result = ERROR;
353*2d543d20SAndroid Build Coastguard Worker 		}
354*2d543d20SAndroid Build Coastguard Worker 	}
355*2d543d20SAndroid Build Coastguard Worker 	free(xattr_digest);
356*2d543d20SAndroid Build Coastguard Worker 	free(calculated_digest);
357*2d543d20SAndroid Build Coastguard Worker 
358*2d543d20SAndroid Build Coastguard Worker 	/* Now add entries to link list. */
359*2d543d20SAndroid Build Coastguard Worker 	new_entry = malloc(sizeof(struct dir_xattr));
360*2d543d20SAndroid Build Coastguard Worker 	if (!new_entry) {
361*2d543d20SAndroid Build Coastguard Worker 		free(sha1_buf);
362*2d543d20SAndroid Build Coastguard Worker 		goto oom;
363*2d543d20SAndroid Build Coastguard Worker 	}
364*2d543d20SAndroid Build Coastguard Worker 	new_entry->next = NULL;
365*2d543d20SAndroid Build Coastguard Worker 
366*2d543d20SAndroid Build Coastguard Worker 	new_entry->directory = strdup(directory);
367*2d543d20SAndroid Build Coastguard Worker 	if (!new_entry->directory) {
368*2d543d20SAndroid Build Coastguard Worker 		free(new_entry);
369*2d543d20SAndroid Build Coastguard Worker 		free(sha1_buf);
370*2d543d20SAndroid Build Coastguard Worker 		goto oom;
371*2d543d20SAndroid Build Coastguard Worker 	}
372*2d543d20SAndroid Build Coastguard Worker 
373*2d543d20SAndroid Build Coastguard Worker 	new_entry->digest = strdup(sha1_buf);
374*2d543d20SAndroid Build Coastguard Worker 	if (!new_entry->digest) {
375*2d543d20SAndroid Build Coastguard Worker 		free(new_entry->directory);
376*2d543d20SAndroid Build Coastguard Worker 		free(new_entry);
377*2d543d20SAndroid Build Coastguard Worker 		free(sha1_buf);
378*2d543d20SAndroid Build Coastguard Worker 		goto oom;
379*2d543d20SAndroid Build Coastguard Worker 	}
380*2d543d20SAndroid Build Coastguard Worker 
381*2d543d20SAndroid Build Coastguard Worker 	new_entry->result = digest_result;
382*2d543d20SAndroid Build Coastguard Worker 
383*2d543d20SAndroid Build Coastguard Worker 	if (!dir_xattr_list) {
384*2d543d20SAndroid Build Coastguard Worker 		dir_xattr_list = new_entry;
385*2d543d20SAndroid Build Coastguard Worker 		dir_xattr_last = new_entry;
386*2d543d20SAndroid Build Coastguard Worker 	} else {
387*2d543d20SAndroid Build Coastguard Worker 		dir_xattr_last->next = new_entry;
388*2d543d20SAndroid Build Coastguard Worker 		dir_xattr_last = new_entry;
389*2d543d20SAndroid Build Coastguard Worker 	}
390*2d543d20SAndroid Build Coastguard Worker 
391*2d543d20SAndroid Build Coastguard Worker 	free(sha1_buf);
392*2d543d20SAndroid Build Coastguard Worker 	return 0;
393*2d543d20SAndroid Build Coastguard Worker 
394*2d543d20SAndroid Build Coastguard Worker oom:
395*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
396*2d543d20SAndroid Build Coastguard Worker 	return -1;
397*2d543d20SAndroid Build Coastguard Worker }
398*2d543d20SAndroid Build Coastguard Worker 
399*2d543d20SAndroid Build Coastguard Worker /*
400*2d543d20SAndroid Build Coastguard Worker  * Support filespec services filespec_add(), filespec_eval() and
401*2d543d20SAndroid Build Coastguard Worker  * filespec_destroy().
402*2d543d20SAndroid Build Coastguard Worker  *
403*2d543d20SAndroid Build Coastguard Worker  * selinux_restorecon(3) uses filespec services when the
404*2d543d20SAndroid Build Coastguard Worker  * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between
405*2d543d20SAndroid Build Coastguard Worker  * an inode and a specification.
406*2d543d20SAndroid Build Coastguard Worker  */
407*2d543d20SAndroid Build Coastguard Worker 
408*2d543d20SAndroid Build Coastguard Worker /*
409*2d543d20SAndroid Build Coastguard Worker  * The hash table of associations, hashed by inode number. Chaining is used
410*2d543d20SAndroid Build Coastguard Worker  * for collisions, with elements ordered by inode number in each bucket.
411*2d543d20SAndroid Build Coastguard Worker  * Each hash bucket has a dummy header.
412*2d543d20SAndroid Build Coastguard Worker  */
413*2d543d20SAndroid Build Coastguard Worker #define HASH_BITS 16
414*2d543d20SAndroid Build Coastguard Worker #define HASH_BUCKETS (1 << HASH_BITS)
415*2d543d20SAndroid Build Coastguard Worker #define HASH_MASK (HASH_BUCKETS-1)
416*2d543d20SAndroid Build Coastguard Worker 
417*2d543d20SAndroid Build Coastguard Worker /*
418*2d543d20SAndroid Build Coastguard Worker  * An association between an inode and a context.
419*2d543d20SAndroid Build Coastguard Worker  */
420*2d543d20SAndroid Build Coastguard Worker typedef struct file_spec {
421*2d543d20SAndroid Build Coastguard Worker 	ino_t ino;		/* inode number */
422*2d543d20SAndroid Build Coastguard Worker 	char *con;		/* matched context */
423*2d543d20SAndroid Build Coastguard Worker 	char *file;		/* full pathname */
424*2d543d20SAndroid Build Coastguard Worker 	struct file_spec *next;	/* next association in hash bucket chain */
425*2d543d20SAndroid Build Coastguard Worker } file_spec_t;
426*2d543d20SAndroid Build Coastguard Worker 
427*2d543d20SAndroid Build Coastguard Worker static file_spec_t *fl_head;
428*2d543d20SAndroid Build Coastguard Worker static pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER;
429*2d543d20SAndroid Build Coastguard Worker 
430*2d543d20SAndroid Build Coastguard Worker /*
431*2d543d20SAndroid Build Coastguard Worker  * Try to add an association between an inode and a context. If there is a
432*2d543d20SAndroid Build Coastguard Worker  * different context that matched the inode, then use the first context
433*2d543d20SAndroid Build Coastguard Worker  * that matched.
434*2d543d20SAndroid Build Coastguard Worker  */
filespec_add(ino_t ino,const char * con,const char * file,const struct rest_flags * flags)435*2d543d20SAndroid Build Coastguard Worker static int filespec_add(ino_t ino, const char *con, const char *file,
436*2d543d20SAndroid Build Coastguard Worker 			const struct rest_flags *flags)
437*2d543d20SAndroid Build Coastguard Worker {
438*2d543d20SAndroid Build Coastguard Worker 	file_spec_t *prevfl, *fl;
439*2d543d20SAndroid Build Coastguard Worker 	uint32_t h;
440*2d543d20SAndroid Build Coastguard Worker 	int ret;
441*2d543d20SAndroid Build Coastguard Worker 	struct stat64 sb;
442*2d543d20SAndroid Build Coastguard Worker 
443*2d543d20SAndroid Build Coastguard Worker 	__pthread_mutex_lock(&fl_mutex);
444*2d543d20SAndroid Build Coastguard Worker 
445*2d543d20SAndroid Build Coastguard Worker 	if (!fl_head) {
446*2d543d20SAndroid Build Coastguard Worker 		fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t));
447*2d543d20SAndroid Build Coastguard Worker 		if (!fl_head)
448*2d543d20SAndroid Build Coastguard Worker 			goto oom;
449*2d543d20SAndroid Build Coastguard Worker 	}
450*2d543d20SAndroid Build Coastguard Worker 
451*2d543d20SAndroid Build Coastguard Worker 	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
452*2d543d20SAndroid Build Coastguard Worker 	for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
453*2d543d20SAndroid Build Coastguard Worker 	     prevfl = fl, fl = fl->next) {
454*2d543d20SAndroid Build Coastguard Worker 		if (ino == fl->ino) {
455*2d543d20SAndroid Build Coastguard Worker 			ret = lstat64(fl->file, &sb);
456*2d543d20SAndroid Build Coastguard Worker 			if (ret < 0 || sb.st_ino != ino) {
457*2d543d20SAndroid Build Coastguard Worker 				freecon(fl->con);
458*2d543d20SAndroid Build Coastguard Worker 				free(fl->file);
459*2d543d20SAndroid Build Coastguard Worker 				fl->file = strdup(file);
460*2d543d20SAndroid Build Coastguard Worker 				if (!fl->file)
461*2d543d20SAndroid Build Coastguard Worker 					goto oom;
462*2d543d20SAndroid Build Coastguard Worker 				fl->con = strdup(con);
463*2d543d20SAndroid Build Coastguard Worker 				if (!fl->con)
464*2d543d20SAndroid Build Coastguard Worker 					goto oom;
465*2d543d20SAndroid Build Coastguard Worker 				goto unlock_1;
466*2d543d20SAndroid Build Coastguard Worker 			}
467*2d543d20SAndroid Build Coastguard Worker 
468*2d543d20SAndroid Build Coastguard Worker 			if (strcmp(fl->con, con) == 0)
469*2d543d20SAndroid Build Coastguard Worker 				goto unlock_1;
470*2d543d20SAndroid Build Coastguard Worker 
471*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
472*2d543d20SAndroid Build Coastguard Worker 				"conflicting specifications for %s and %s, using %s.\n",
473*2d543d20SAndroid Build Coastguard Worker 				file, fl->file, fl->con);
474*2d543d20SAndroid Build Coastguard Worker 			free(fl->file);
475*2d543d20SAndroid Build Coastguard Worker 			fl->file = strdup(file);
476*2d543d20SAndroid Build Coastguard Worker 			if (!fl->file)
477*2d543d20SAndroid Build Coastguard Worker 				goto oom;
478*2d543d20SAndroid Build Coastguard Worker 
479*2d543d20SAndroid Build Coastguard Worker 			__pthread_mutex_unlock(&fl_mutex);
480*2d543d20SAndroid Build Coastguard Worker 
481*2d543d20SAndroid Build Coastguard Worker 			if (flags->conflicterror) {
482*2d543d20SAndroid Build Coastguard Worker 				selinux_log(SELINUX_ERROR,
483*2d543d20SAndroid Build Coastguard Worker 				"treating conflicting specifications as an error.\n");
484*2d543d20SAndroid Build Coastguard Worker 				return -1;
485*2d543d20SAndroid Build Coastguard Worker 			}
486*2d543d20SAndroid Build Coastguard Worker 			return 1;
487*2d543d20SAndroid Build Coastguard Worker 		}
488*2d543d20SAndroid Build Coastguard Worker 
489*2d543d20SAndroid Build Coastguard Worker 		if (ino > fl->ino)
490*2d543d20SAndroid Build Coastguard Worker 			break;
491*2d543d20SAndroid Build Coastguard Worker 	}
492*2d543d20SAndroid Build Coastguard Worker 
493*2d543d20SAndroid Build Coastguard Worker 	fl = malloc(sizeof(file_spec_t));
494*2d543d20SAndroid Build Coastguard Worker 	if (!fl)
495*2d543d20SAndroid Build Coastguard Worker 		goto oom;
496*2d543d20SAndroid Build Coastguard Worker 	fl->ino = ino;
497*2d543d20SAndroid Build Coastguard Worker 	fl->con = strdup(con);
498*2d543d20SAndroid Build Coastguard Worker 	if (!fl->con)
499*2d543d20SAndroid Build Coastguard Worker 		goto oom_freefl;
500*2d543d20SAndroid Build Coastguard Worker 	fl->file = strdup(file);
501*2d543d20SAndroid Build Coastguard Worker 	if (!fl->file)
502*2d543d20SAndroid Build Coastguard Worker 		goto oom_freeflcon;
503*2d543d20SAndroid Build Coastguard Worker 	fl->next = prevfl->next;
504*2d543d20SAndroid Build Coastguard Worker 	prevfl->next = fl;
505*2d543d20SAndroid Build Coastguard Worker 
506*2d543d20SAndroid Build Coastguard Worker 	__pthread_mutex_unlock(&fl_mutex);
507*2d543d20SAndroid Build Coastguard Worker 	return 0;
508*2d543d20SAndroid Build Coastguard Worker 
509*2d543d20SAndroid Build Coastguard Worker oom_freeflcon:
510*2d543d20SAndroid Build Coastguard Worker 	free(fl->con);
511*2d543d20SAndroid Build Coastguard Worker oom_freefl:
512*2d543d20SAndroid Build Coastguard Worker 	free(fl);
513*2d543d20SAndroid Build Coastguard Worker oom:
514*2d543d20SAndroid Build Coastguard Worker 	__pthread_mutex_unlock(&fl_mutex);
515*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
516*2d543d20SAndroid Build Coastguard Worker 	return -1;
517*2d543d20SAndroid Build Coastguard Worker unlock_1:
518*2d543d20SAndroid Build Coastguard Worker 	__pthread_mutex_unlock(&fl_mutex);
519*2d543d20SAndroid Build Coastguard Worker 	return 1;
520*2d543d20SAndroid Build Coastguard Worker }
521*2d543d20SAndroid Build Coastguard Worker 
522*2d543d20SAndroid Build Coastguard Worker /*
523*2d543d20SAndroid Build Coastguard Worker  * Evaluate the association hash table distribution.
524*2d543d20SAndroid Build Coastguard Worker  */
525*2d543d20SAndroid Build Coastguard Worker #ifdef DEBUG
filespec_eval(void)526*2d543d20SAndroid Build Coastguard Worker static void filespec_eval(void)
527*2d543d20SAndroid Build Coastguard Worker {
528*2d543d20SAndroid Build Coastguard Worker 	file_spec_t *fl;
529*2d543d20SAndroid Build Coastguard Worker 	uint32_t h;
530*2d543d20SAndroid Build Coastguard Worker 	size_t used, nel, len, longest;
531*2d543d20SAndroid Build Coastguard Worker 
532*2d543d20SAndroid Build Coastguard Worker 	if (!fl_head)
533*2d543d20SAndroid Build Coastguard Worker 		return;
534*2d543d20SAndroid Build Coastguard Worker 
535*2d543d20SAndroid Build Coastguard Worker 	used = 0;
536*2d543d20SAndroid Build Coastguard Worker 	longest = 0;
537*2d543d20SAndroid Build Coastguard Worker 	nel = 0;
538*2d543d20SAndroid Build Coastguard Worker 	for (h = 0; h < HASH_BUCKETS; h++) {
539*2d543d20SAndroid Build Coastguard Worker 		len = 0;
540*2d543d20SAndroid Build Coastguard Worker 		for (fl = fl_head[h].next; fl; fl = fl->next)
541*2d543d20SAndroid Build Coastguard Worker 			len++;
542*2d543d20SAndroid Build Coastguard Worker 		if (len)
543*2d543d20SAndroid Build Coastguard Worker 			used++;
544*2d543d20SAndroid Build Coastguard Worker 		if (len > longest)
545*2d543d20SAndroid Build Coastguard Worker 			longest = len;
546*2d543d20SAndroid Build Coastguard Worker 		nel += len;
547*2d543d20SAndroid Build Coastguard Worker 	}
548*2d543d20SAndroid Build Coastguard Worker 
549*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_INFO,
550*2d543d20SAndroid Build Coastguard Worker 		     "filespec hash table stats: %zu elements, %zu/%zu buckets used, longest chain length %zu\n",
551*2d543d20SAndroid Build Coastguard Worker 		     nel, used, HASH_BUCKETS, longest);
552*2d543d20SAndroid Build Coastguard Worker }
553*2d543d20SAndroid Build Coastguard Worker #else
filespec_eval(void)554*2d543d20SAndroid Build Coastguard Worker static void filespec_eval(void)
555*2d543d20SAndroid Build Coastguard Worker {
556*2d543d20SAndroid Build Coastguard Worker }
557*2d543d20SAndroid Build Coastguard Worker #endif
558*2d543d20SAndroid Build Coastguard Worker 
559*2d543d20SAndroid Build Coastguard Worker /*
560*2d543d20SAndroid Build Coastguard Worker  * Destroy the association hash table.
561*2d543d20SAndroid Build Coastguard Worker  */
filespec_destroy(void)562*2d543d20SAndroid Build Coastguard Worker static void filespec_destroy(void)
563*2d543d20SAndroid Build Coastguard Worker {
564*2d543d20SAndroid Build Coastguard Worker 	file_spec_t *fl, *tmp;
565*2d543d20SAndroid Build Coastguard Worker 	uint32_t h;
566*2d543d20SAndroid Build Coastguard Worker 
567*2d543d20SAndroid Build Coastguard Worker 	if (!fl_head)
568*2d543d20SAndroid Build Coastguard Worker 		return;
569*2d543d20SAndroid Build Coastguard Worker 
570*2d543d20SAndroid Build Coastguard Worker 	for (h = 0; h < HASH_BUCKETS; h++) {
571*2d543d20SAndroid Build Coastguard Worker 		fl = fl_head[h].next;
572*2d543d20SAndroid Build Coastguard Worker 		while (fl) {
573*2d543d20SAndroid Build Coastguard Worker 			tmp = fl;
574*2d543d20SAndroid Build Coastguard Worker 			fl = fl->next;
575*2d543d20SAndroid Build Coastguard Worker 			freecon(tmp->con);
576*2d543d20SAndroid Build Coastguard Worker 			free(tmp->file);
577*2d543d20SAndroid Build Coastguard Worker 			free(tmp);
578*2d543d20SAndroid Build Coastguard Worker 		}
579*2d543d20SAndroid Build Coastguard Worker 		fl_head[h].next = NULL;
580*2d543d20SAndroid Build Coastguard Worker 	}
581*2d543d20SAndroid Build Coastguard Worker 	free(fl_head);
582*2d543d20SAndroid Build Coastguard Worker 	fl_head = NULL;
583*2d543d20SAndroid Build Coastguard Worker }
584*2d543d20SAndroid Build Coastguard Worker 
585*2d543d20SAndroid Build Coastguard Worker /*
586*2d543d20SAndroid Build Coastguard Worker  * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if
587*2d543d20SAndroid Build Coastguard Worker  * the type components differ, updating newtypecon if so.
588*2d543d20SAndroid Build Coastguard Worker  */
compare_types(const char * curcon,const char * newcon,char ** newtypecon)589*2d543d20SAndroid Build Coastguard Worker static int compare_types(const char *curcon, const char *newcon, char **newtypecon)
590*2d543d20SAndroid Build Coastguard Worker {
591*2d543d20SAndroid Build Coastguard Worker 	int types_differ = 0;
592*2d543d20SAndroid Build Coastguard Worker 	context_t cona;
593*2d543d20SAndroid Build Coastguard Worker 	context_t conb;
594*2d543d20SAndroid Build Coastguard Worker 	int rc = 0;
595*2d543d20SAndroid Build Coastguard Worker 
596*2d543d20SAndroid Build Coastguard Worker 	cona = context_new(curcon);
597*2d543d20SAndroid Build Coastguard Worker 	if (!cona) {
598*2d543d20SAndroid Build Coastguard Worker 		rc = -1;
599*2d543d20SAndroid Build Coastguard Worker 		goto out;
600*2d543d20SAndroid Build Coastguard Worker 	}
601*2d543d20SAndroid Build Coastguard Worker 	conb = context_new(newcon);
602*2d543d20SAndroid Build Coastguard Worker 	if (!conb) {
603*2d543d20SAndroid Build Coastguard Worker 		context_free(cona);
604*2d543d20SAndroid Build Coastguard Worker 		rc = -1;
605*2d543d20SAndroid Build Coastguard Worker 		goto out;
606*2d543d20SAndroid Build Coastguard Worker 	}
607*2d543d20SAndroid Build Coastguard Worker 
608*2d543d20SAndroid Build Coastguard Worker 	types_differ = strcmp(context_type_get(cona), context_type_get(conb));
609*2d543d20SAndroid Build Coastguard Worker 	if (types_differ) {
610*2d543d20SAndroid Build Coastguard Worker 		rc |= context_user_set(conb, context_user_get(cona));
611*2d543d20SAndroid Build Coastguard Worker 		rc |= context_role_set(conb, context_role_get(cona));
612*2d543d20SAndroid Build Coastguard Worker 		rc |= context_range_set(conb, context_range_get(cona));
613*2d543d20SAndroid Build Coastguard Worker 		if (!rc) {
614*2d543d20SAndroid Build Coastguard Worker 			*newtypecon = strdup(context_str(conb));
615*2d543d20SAndroid Build Coastguard Worker 			if (!*newtypecon) {
616*2d543d20SAndroid Build Coastguard Worker 				rc = -1;
617*2d543d20SAndroid Build Coastguard Worker 				goto err;
618*2d543d20SAndroid Build Coastguard Worker 			}
619*2d543d20SAndroid Build Coastguard Worker 		}
620*2d543d20SAndroid Build Coastguard Worker 	}
621*2d543d20SAndroid Build Coastguard Worker 
622*2d543d20SAndroid Build Coastguard Worker err:
623*2d543d20SAndroid Build Coastguard Worker 	context_free(cona);
624*2d543d20SAndroid Build Coastguard Worker 	context_free(conb);
625*2d543d20SAndroid Build Coastguard Worker out:
626*2d543d20SAndroid Build Coastguard Worker 	return rc;
627*2d543d20SAndroid Build Coastguard Worker }
628*2d543d20SAndroid Build Coastguard Worker 
restorecon_sb(const char * pathname,const struct stat * sb,const struct rest_flags * flags,bool first)629*2d543d20SAndroid Build Coastguard Worker static int restorecon_sb(const char *pathname, const struct stat *sb,
630*2d543d20SAndroid Build Coastguard Worker 			    const struct rest_flags *flags, bool first)
631*2d543d20SAndroid Build Coastguard Worker {
632*2d543d20SAndroid Build Coastguard Worker 	char *newcon = NULL;
633*2d543d20SAndroid Build Coastguard Worker 	char *curcon = NULL;
634*2d543d20SAndroid Build Coastguard Worker 	char *newtypecon = NULL;
635*2d543d20SAndroid Build Coastguard Worker 	int rc;
636*2d543d20SAndroid Build Coastguard Worker 	const char *lookup_path = pathname;
637*2d543d20SAndroid Build Coastguard Worker 
638*2d543d20SAndroid Build Coastguard Worker 	if (rootpath) {
639*2d543d20SAndroid Build Coastguard Worker 		if (strncmp(rootpath, lookup_path, rootpathlen) != 0) {
640*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
641*2d543d20SAndroid Build Coastguard Worker 				    "%s is not located in alt_rootpath %s\n",
642*2d543d20SAndroid Build Coastguard Worker 				    lookup_path, rootpath);
643*2d543d20SAndroid Build Coastguard Worker 			return -1;
644*2d543d20SAndroid Build Coastguard Worker 		}
645*2d543d20SAndroid Build Coastguard Worker 		lookup_path += rootpathlen;
646*2d543d20SAndroid Build Coastguard Worker 	}
647*2d543d20SAndroid Build Coastguard Worker 
648*2d543d20SAndroid Build Coastguard Worker 	if (rootpath != NULL && lookup_path[0] == '\0')
649*2d543d20SAndroid Build Coastguard Worker 		/* this is actually the root dir of the alt root. */
650*2d543d20SAndroid Build Coastguard Worker 		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
651*2d543d20SAndroid Build Coastguard Worker 						    sb->st_mode & S_IFMT);
652*2d543d20SAndroid Build Coastguard Worker 	else
653*2d543d20SAndroid Build Coastguard Worker 		rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path,
654*2d543d20SAndroid Build Coastguard Worker 						    sb->st_mode & S_IFMT);
655*2d543d20SAndroid Build Coastguard Worker 
656*2d543d20SAndroid Build Coastguard Worker 	if (rc < 0) {
657*2d543d20SAndroid Build Coastguard Worker 		if (errno == ENOENT) {
658*2d543d20SAndroid Build Coastguard Worker 			if (flags->warnonnomatch && first)
659*2d543d20SAndroid Build Coastguard Worker 				selinux_log(SELINUX_INFO,
660*2d543d20SAndroid Build Coastguard Worker 					    "Warning no default label for %s\n",
661*2d543d20SAndroid Build Coastguard Worker 					    lookup_path);
662*2d543d20SAndroid Build Coastguard Worker 
663*2d543d20SAndroid Build Coastguard Worker 			return 0; /* no match, but not an error */
664*2d543d20SAndroid Build Coastguard Worker 		}
665*2d543d20SAndroid Build Coastguard Worker 
666*2d543d20SAndroid Build Coastguard Worker 		return -1;
667*2d543d20SAndroid Build Coastguard Worker 	}
668*2d543d20SAndroid Build Coastguard Worker 
669*2d543d20SAndroid Build Coastguard Worker 	if (flags->progress) {
670*2d543d20SAndroid Build Coastguard Worker 		__pthread_mutex_lock(&progress_mutex);
671*2d543d20SAndroid Build Coastguard Worker 		fc_count++;
672*2d543d20SAndroid Build Coastguard Worker 		if (fc_count % STAR_COUNT == 0) {
673*2d543d20SAndroid Build Coastguard Worker 			if (flags->mass_relabel && efile_count > 0) {
674*2d543d20SAndroid Build Coastguard Worker 				float pc = (fc_count < efile_count) ? (100.0 *
675*2d543d20SAndroid Build Coastguard Worker 					     fc_count / efile_count) : 100;
676*2d543d20SAndroid Build Coastguard Worker 				fprintf(stdout, "\r%-.1f%%", (double)pc);
677*2d543d20SAndroid Build Coastguard Worker 			} else {
678*2d543d20SAndroid Build Coastguard Worker 				fprintf(stdout, "\r%" PRIu64 "k", fc_count / STAR_COUNT);
679*2d543d20SAndroid Build Coastguard Worker 			}
680*2d543d20SAndroid Build Coastguard Worker 			fflush(stdout);
681*2d543d20SAndroid Build Coastguard Worker 		}
682*2d543d20SAndroid Build Coastguard Worker 		__pthread_mutex_unlock(&progress_mutex);
683*2d543d20SAndroid Build Coastguard Worker 	}
684*2d543d20SAndroid Build Coastguard Worker 
685*2d543d20SAndroid Build Coastguard Worker 	if (flags->add_assoc) {
686*2d543d20SAndroid Build Coastguard Worker 		rc = filespec_add(sb->st_ino, newcon, pathname, flags);
687*2d543d20SAndroid Build Coastguard Worker 
688*2d543d20SAndroid Build Coastguard Worker 		if (rc < 0) {
689*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
690*2d543d20SAndroid Build Coastguard Worker 				    "filespec_add error: %s\n", pathname);
691*2d543d20SAndroid Build Coastguard Worker 			freecon(newcon);
692*2d543d20SAndroid Build Coastguard Worker 			return -1;
693*2d543d20SAndroid Build Coastguard Worker 		}
694*2d543d20SAndroid Build Coastguard Worker 
695*2d543d20SAndroid Build Coastguard Worker 		if (rc > 0) {
696*2d543d20SAndroid Build Coastguard Worker 			/* Already an association and it took precedence. */
697*2d543d20SAndroid Build Coastguard Worker 			freecon(newcon);
698*2d543d20SAndroid Build Coastguard Worker 			return 0;
699*2d543d20SAndroid Build Coastguard Worker 		}
700*2d543d20SAndroid Build Coastguard Worker 	}
701*2d543d20SAndroid Build Coastguard Worker 
702*2d543d20SAndroid Build Coastguard Worker 	if (flags->log_matches)
703*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_INFO, "%s matched by %s\n",
704*2d543d20SAndroid Build Coastguard Worker 			    pathname, newcon);
705*2d543d20SAndroid Build Coastguard Worker 
706*2d543d20SAndroid Build Coastguard Worker 	if (lgetfilecon_raw(pathname, &curcon) < 0) {
707*2d543d20SAndroid Build Coastguard Worker 		if (errno != ENODATA)
708*2d543d20SAndroid Build Coastguard Worker 			goto err;
709*2d543d20SAndroid Build Coastguard Worker 
710*2d543d20SAndroid Build Coastguard Worker 		curcon = NULL;
711*2d543d20SAndroid Build Coastguard Worker 	}
712*2d543d20SAndroid Build Coastguard Worker 
713*2d543d20SAndroid Build Coastguard Worker 	if (curcon == NULL || strcmp(curcon, newcon) != 0) {
714*2d543d20SAndroid Build Coastguard Worker 		bool updated = false;
715*2d543d20SAndroid Build Coastguard Worker 
716*2d543d20SAndroid Build Coastguard Worker 		if (!flags->set_specctx && curcon &&
717*2d543d20SAndroid Build Coastguard Worker 				    (is_context_customizable(curcon) > 0)) {
718*2d543d20SAndroid Build Coastguard Worker 			if (flags->verbose) {
719*2d543d20SAndroid Build Coastguard Worker 				selinux_log(SELINUX_INFO,
720*2d543d20SAndroid Build Coastguard Worker 				 "%s not reset as customized by admin to %s\n",
721*2d543d20SAndroid Build Coastguard Worker 							    pathname, curcon);
722*2d543d20SAndroid Build Coastguard Worker 			}
723*2d543d20SAndroid Build Coastguard Worker 			goto out;
724*2d543d20SAndroid Build Coastguard Worker 		}
725*2d543d20SAndroid Build Coastguard Worker 
726*2d543d20SAndroid Build Coastguard Worker 		if (!flags->set_specctx && curcon) {
727*2d543d20SAndroid Build Coastguard Worker 			/* If types different then update newcon. */
728*2d543d20SAndroid Build Coastguard Worker 			rc = compare_types(curcon, newcon, &newtypecon);
729*2d543d20SAndroid Build Coastguard Worker 			if (rc)
730*2d543d20SAndroid Build Coastguard Worker 				goto err;
731*2d543d20SAndroid Build Coastguard Worker 
732*2d543d20SAndroid Build Coastguard Worker 			if (newtypecon) {
733*2d543d20SAndroid Build Coastguard Worker 				freecon(newcon);
734*2d543d20SAndroid Build Coastguard Worker 				newcon = newtypecon;
735*2d543d20SAndroid Build Coastguard Worker 			} else {
736*2d543d20SAndroid Build Coastguard Worker 				goto out;
737*2d543d20SAndroid Build Coastguard Worker 			}
738*2d543d20SAndroid Build Coastguard Worker 		}
739*2d543d20SAndroid Build Coastguard Worker 
740*2d543d20SAndroid Build Coastguard Worker 		if (!flags->nochange) {
741*2d543d20SAndroid Build Coastguard Worker 			if (lsetfilecon(pathname, newcon) < 0)
742*2d543d20SAndroid Build Coastguard Worker 				goto err;
743*2d543d20SAndroid Build Coastguard Worker 			updated = true;
744*2d543d20SAndroid Build Coastguard Worker 		}
745*2d543d20SAndroid Build Coastguard Worker 
746*2d543d20SAndroid Build Coastguard Worker 		if (flags->verbose)
747*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_INFO,
748*2d543d20SAndroid Build Coastguard Worker 				    "%s %s from %s to %s\n",
749*2d543d20SAndroid Build Coastguard Worker 				    updated ? "Relabeled" : "Would relabel",
750*2d543d20SAndroid Build Coastguard Worker 				    pathname,
751*2d543d20SAndroid Build Coastguard Worker 				    curcon ? curcon : "<no context>",
752*2d543d20SAndroid Build Coastguard Worker 				    newcon);
753*2d543d20SAndroid Build Coastguard Worker 
754*2d543d20SAndroid Build Coastguard Worker 		if (flags->syslog_changes && !flags->nochange) {
755*2d543d20SAndroid Build Coastguard Worker 			if (curcon)
756*2d543d20SAndroid Build Coastguard Worker 				syslog(LOG_INFO,
757*2d543d20SAndroid Build Coastguard Worker 					    "relabeling %s from %s to %s\n",
758*2d543d20SAndroid Build Coastguard Worker 					    pathname, curcon, newcon);
759*2d543d20SAndroid Build Coastguard Worker 			else
760*2d543d20SAndroid Build Coastguard Worker 				syslog(LOG_INFO, "labeling %s to %s\n",
761*2d543d20SAndroid Build Coastguard Worker 					    pathname, newcon);
762*2d543d20SAndroid Build Coastguard Worker 		}
763*2d543d20SAndroid Build Coastguard Worker 	}
764*2d543d20SAndroid Build Coastguard Worker 
765*2d543d20SAndroid Build Coastguard Worker out:
766*2d543d20SAndroid Build Coastguard Worker 	rc = 0;
767*2d543d20SAndroid Build Coastguard Worker out1:
768*2d543d20SAndroid Build Coastguard Worker 	freecon(curcon);
769*2d543d20SAndroid Build Coastguard Worker 	freecon(newcon);
770*2d543d20SAndroid Build Coastguard Worker 	return rc;
771*2d543d20SAndroid Build Coastguard Worker err:
772*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR,
773*2d543d20SAndroid Build Coastguard Worker 		    "Could not set context for %s:  %m\n",
774*2d543d20SAndroid Build Coastguard Worker 		    pathname);
775*2d543d20SAndroid Build Coastguard Worker 	rc = -1;
776*2d543d20SAndroid Build Coastguard Worker 	goto out1;
777*2d543d20SAndroid Build Coastguard Worker }
778*2d543d20SAndroid Build Coastguard Worker 
779*2d543d20SAndroid Build Coastguard Worker struct dir_hash_node {
780*2d543d20SAndroid Build Coastguard Worker 	char *path;
781*2d543d20SAndroid Build Coastguard Worker 	uint8_t digest[SHA1_HASH_SIZE];
782*2d543d20SAndroid Build Coastguard Worker 	struct dir_hash_node *next;
783*2d543d20SAndroid Build Coastguard Worker };
784*2d543d20SAndroid Build Coastguard Worker /*
785*2d543d20SAndroid Build Coastguard Worker  * Returns true if the digest of all partial matched contexts is the same as
786*2d543d20SAndroid Build Coastguard Worker  * the one saved by setxattr. Otherwise returns false and constructs a
787*2d543d20SAndroid Build Coastguard Worker  * dir_hash_node with the newly calculated digest.
788*2d543d20SAndroid Build Coastguard Worker  */
check_context_match_for_dir(const char * pathname,struct dir_hash_node ** new_node,int error)789*2d543d20SAndroid Build Coastguard Worker static bool check_context_match_for_dir(const char *pathname,
790*2d543d20SAndroid Build Coastguard Worker 					struct dir_hash_node **new_node,
791*2d543d20SAndroid Build Coastguard Worker 					int error)
792*2d543d20SAndroid Build Coastguard Worker {
793*2d543d20SAndroid Build Coastguard Worker 	bool status;
794*2d543d20SAndroid Build Coastguard Worker 	size_t digest_len = 0;
795*2d543d20SAndroid Build Coastguard Worker 	uint8_t *read_digest = NULL;
796*2d543d20SAndroid Build Coastguard Worker 	uint8_t *calculated_digest = NULL;
797*2d543d20SAndroid Build Coastguard Worker 
798*2d543d20SAndroid Build Coastguard Worker 	if (!new_node)
799*2d543d20SAndroid Build Coastguard Worker 		return false;
800*2d543d20SAndroid Build Coastguard Worker 
801*2d543d20SAndroid Build Coastguard Worker 	*new_node = NULL;
802*2d543d20SAndroid Build Coastguard Worker 
803*2d543d20SAndroid Build Coastguard Worker 	/* status = true if digests match, false otherwise. */
804*2d543d20SAndroid Build Coastguard Worker 	status = selabel_get_digests_all_partial_matches(fc_sehandle, pathname,
805*2d543d20SAndroid Build Coastguard Worker 							 &calculated_digest,
806*2d543d20SAndroid Build Coastguard Worker 							 &read_digest,
807*2d543d20SAndroid Build Coastguard Worker 							 &digest_len);
808*2d543d20SAndroid Build Coastguard Worker 
809*2d543d20SAndroid Build Coastguard Worker 	if (status)
810*2d543d20SAndroid Build Coastguard Worker 		goto free;
811*2d543d20SAndroid Build Coastguard Worker 
812*2d543d20SAndroid Build Coastguard Worker 	/* Save digest of all matched contexts for the current directory. */
813*2d543d20SAndroid Build Coastguard Worker 	if (!error && calculated_digest) {
814*2d543d20SAndroid Build Coastguard Worker 		*new_node = calloc(1, sizeof(struct dir_hash_node));
815*2d543d20SAndroid Build Coastguard Worker 
816*2d543d20SAndroid Build Coastguard Worker 		if (!*new_node)
817*2d543d20SAndroid Build Coastguard Worker 			goto oom;
818*2d543d20SAndroid Build Coastguard Worker 
819*2d543d20SAndroid Build Coastguard Worker 		(*new_node)->path = strdup(pathname);
820*2d543d20SAndroid Build Coastguard Worker 
821*2d543d20SAndroid Build Coastguard Worker 		if (!(*new_node)->path) {
822*2d543d20SAndroid Build Coastguard Worker 			free(*new_node);
823*2d543d20SAndroid Build Coastguard Worker 			*new_node = NULL;
824*2d543d20SAndroid Build Coastguard Worker 			goto oom;
825*2d543d20SAndroid Build Coastguard Worker 		}
826*2d543d20SAndroid Build Coastguard Worker 		memcpy((*new_node)->digest, calculated_digest, digest_len);
827*2d543d20SAndroid Build Coastguard Worker 		(*new_node)->next = NULL;
828*2d543d20SAndroid Build Coastguard Worker 	}
829*2d543d20SAndroid Build Coastguard Worker 
830*2d543d20SAndroid Build Coastguard Worker free:
831*2d543d20SAndroid Build Coastguard Worker 	free(calculated_digest);
832*2d543d20SAndroid Build Coastguard Worker 	free(read_digest);
833*2d543d20SAndroid Build Coastguard Worker 	return status;
834*2d543d20SAndroid Build Coastguard Worker 
835*2d543d20SAndroid Build Coastguard Worker oom:
836*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
837*2d543d20SAndroid Build Coastguard Worker 	goto free;
838*2d543d20SAndroid Build Coastguard Worker }
839*2d543d20SAndroid Build Coastguard Worker 
840*2d543d20SAndroid Build Coastguard Worker struct rest_state {
841*2d543d20SAndroid Build Coastguard Worker 	struct rest_flags flags;
842*2d543d20SAndroid Build Coastguard Worker 	dev_t dev_num;
843*2d543d20SAndroid Build Coastguard Worker 	struct statfs sfsb;
844*2d543d20SAndroid Build Coastguard Worker 	bool ignore_digest;
845*2d543d20SAndroid Build Coastguard Worker 	bool setrestorecondigest;
846*2d543d20SAndroid Build Coastguard Worker 	bool parallel;
847*2d543d20SAndroid Build Coastguard Worker 
848*2d543d20SAndroid Build Coastguard Worker 	FTS *fts;
849*2d543d20SAndroid Build Coastguard Worker 	FTSENT *ftsent_first;
850*2d543d20SAndroid Build Coastguard Worker 	struct dir_hash_node *head, *current;
851*2d543d20SAndroid Build Coastguard Worker 	bool abort;
852*2d543d20SAndroid Build Coastguard Worker 	int error;
853*2d543d20SAndroid Build Coastguard Worker 	long unsigned skipped_errors;
854*2d543d20SAndroid Build Coastguard Worker 	int saved_errno;
855*2d543d20SAndroid Build Coastguard Worker 	pthread_mutex_t mutex;
856*2d543d20SAndroid Build Coastguard Worker };
857*2d543d20SAndroid Build Coastguard Worker 
selinux_restorecon_thread(void * arg)858*2d543d20SAndroid Build Coastguard Worker static void *selinux_restorecon_thread(void *arg)
859*2d543d20SAndroid Build Coastguard Worker {
860*2d543d20SAndroid Build Coastguard Worker 	struct rest_state *state = arg;
861*2d543d20SAndroid Build Coastguard Worker 	FTS *fts = state->fts;
862*2d543d20SAndroid Build Coastguard Worker 	FTSENT *ftsent;
863*2d543d20SAndroid Build Coastguard Worker 	int error;
864*2d543d20SAndroid Build Coastguard Worker 	char ent_path[PATH_MAX];
865*2d543d20SAndroid Build Coastguard Worker 	struct stat ent_st;
866*2d543d20SAndroid Build Coastguard Worker 	bool first = false;
867*2d543d20SAndroid Build Coastguard Worker 
868*2d543d20SAndroid Build Coastguard Worker 	if (state->parallel)
869*2d543d20SAndroid Build Coastguard Worker 		pthread_mutex_lock(&state->mutex);
870*2d543d20SAndroid Build Coastguard Worker 
871*2d543d20SAndroid Build Coastguard Worker 	if (state->ftsent_first) {
872*2d543d20SAndroid Build Coastguard Worker 		ftsent = state->ftsent_first;
873*2d543d20SAndroid Build Coastguard Worker 		state->ftsent_first = NULL;
874*2d543d20SAndroid Build Coastguard Worker 		first = true;
875*2d543d20SAndroid Build Coastguard Worker 		goto loop_body;
876*2d543d20SAndroid Build Coastguard Worker 	}
877*2d543d20SAndroid Build Coastguard Worker 
878*2d543d20SAndroid Build Coastguard Worker 	while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) {
879*2d543d20SAndroid Build Coastguard Worker loop_body:
880*2d543d20SAndroid Build Coastguard Worker 		/* If the FTS_XDEV flag is set and the device is different */
881*2d543d20SAndroid Build Coastguard Worker 		if (state->flags.set_xdev &&
882*2d543d20SAndroid Build Coastguard Worker 		    ftsent->fts_statp->st_dev != state->dev_num)
883*2d543d20SAndroid Build Coastguard Worker 			continue;
884*2d543d20SAndroid Build Coastguard Worker 
885*2d543d20SAndroid Build Coastguard Worker 		switch (ftsent->fts_info) {
886*2d543d20SAndroid Build Coastguard Worker 		case FTS_DC:
887*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
888*2d543d20SAndroid Build Coastguard Worker 				    "Directory cycle on %s.\n",
889*2d543d20SAndroid Build Coastguard Worker 				    ftsent->fts_path);
890*2d543d20SAndroid Build Coastguard Worker 			errno = ELOOP;
891*2d543d20SAndroid Build Coastguard Worker 			state->error = -1;
892*2d543d20SAndroid Build Coastguard Worker 			state->abort = true;
893*2d543d20SAndroid Build Coastguard Worker 			goto finish;
894*2d543d20SAndroid Build Coastguard Worker 		case FTS_DP:
895*2d543d20SAndroid Build Coastguard Worker 			continue;
896*2d543d20SAndroid Build Coastguard Worker 		case FTS_DNR:
897*2d543d20SAndroid Build Coastguard Worker 			error = errno;
898*2d543d20SAndroid Build Coastguard Worker 			errno = ftsent->fts_errno;
899*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
900*2d543d20SAndroid Build Coastguard Worker 				    "Could not read %s: %m.\n",
901*2d543d20SAndroid Build Coastguard Worker 				    ftsent->fts_path);
902*2d543d20SAndroid Build Coastguard Worker 			errno = error;
903*2d543d20SAndroid Build Coastguard Worker 			fts_set(fts, ftsent, FTS_SKIP);
904*2d543d20SAndroid Build Coastguard Worker 			continue;
905*2d543d20SAndroid Build Coastguard Worker 		case FTS_NS:
906*2d543d20SAndroid Build Coastguard Worker 			error = errno;
907*2d543d20SAndroid Build Coastguard Worker 			errno = ftsent->fts_errno;
908*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
909*2d543d20SAndroid Build Coastguard Worker 				    "Could not stat %s: %m.\n",
910*2d543d20SAndroid Build Coastguard Worker 				    ftsent->fts_path);
911*2d543d20SAndroid Build Coastguard Worker 			errno = error;
912*2d543d20SAndroid Build Coastguard Worker 			fts_set(fts, ftsent, FTS_SKIP);
913*2d543d20SAndroid Build Coastguard Worker 			continue;
914*2d543d20SAndroid Build Coastguard Worker 		case FTS_ERR:
915*2d543d20SAndroid Build Coastguard Worker 			error = errno;
916*2d543d20SAndroid Build Coastguard Worker 			errno = ftsent->fts_errno;
917*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
918*2d543d20SAndroid Build Coastguard Worker 				    "Error on %s: %m.\n",
919*2d543d20SAndroid Build Coastguard Worker 				    ftsent->fts_path);
920*2d543d20SAndroid Build Coastguard Worker 			errno = error;
921*2d543d20SAndroid Build Coastguard Worker 			fts_set(fts, ftsent, FTS_SKIP);
922*2d543d20SAndroid Build Coastguard Worker 			continue;
923*2d543d20SAndroid Build Coastguard Worker 		case FTS_D:
924*2d543d20SAndroid Build Coastguard Worker 			if (state->sfsb.f_type == SYSFS_MAGIC &&
925*2d543d20SAndroid Build Coastguard Worker 			    !selabel_partial_match(fc_sehandle,
926*2d543d20SAndroid Build Coastguard Worker 			    ftsent->fts_path)) {
927*2d543d20SAndroid Build Coastguard Worker 				fts_set(fts, ftsent, FTS_SKIP);
928*2d543d20SAndroid Build Coastguard Worker 				continue;
929*2d543d20SAndroid Build Coastguard Worker 			}
930*2d543d20SAndroid Build Coastguard Worker 
931*2d543d20SAndroid Build Coastguard Worker 			if (check_excluded(ftsent->fts_path)) {
932*2d543d20SAndroid Build Coastguard Worker 				fts_set(fts, ftsent, FTS_SKIP);
933*2d543d20SAndroid Build Coastguard Worker 				continue;
934*2d543d20SAndroid Build Coastguard Worker 			}
935*2d543d20SAndroid Build Coastguard Worker 
936*2d543d20SAndroid Build Coastguard Worker 			if (state->setrestorecondigest) {
937*2d543d20SAndroid Build Coastguard Worker 				struct dir_hash_node *new_node = NULL;
938*2d543d20SAndroid Build Coastguard Worker 
939*2d543d20SAndroid Build Coastguard Worker 				if (check_context_match_for_dir(ftsent->fts_path,
940*2d543d20SAndroid Build Coastguard Worker 								&new_node,
941*2d543d20SAndroid Build Coastguard Worker 								state->error) &&
942*2d543d20SAndroid Build Coastguard Worker 								!state->ignore_digest) {
943*2d543d20SAndroid Build Coastguard Worker 					selinux_log(SELINUX_INFO,
944*2d543d20SAndroid Build Coastguard Worker 						"Skipping restorecon on directory(%s)\n",
945*2d543d20SAndroid Build Coastguard Worker 						    ftsent->fts_path);
946*2d543d20SAndroid Build Coastguard Worker 					fts_set(fts, ftsent, FTS_SKIP);
947*2d543d20SAndroid Build Coastguard Worker 					continue;
948*2d543d20SAndroid Build Coastguard Worker 				}
949*2d543d20SAndroid Build Coastguard Worker 
950*2d543d20SAndroid Build Coastguard Worker 				if (new_node && !state->error) {
951*2d543d20SAndroid Build Coastguard Worker 					if (!state->current) {
952*2d543d20SAndroid Build Coastguard Worker 						state->current = new_node;
953*2d543d20SAndroid Build Coastguard Worker 						state->head = state->current;
954*2d543d20SAndroid Build Coastguard Worker 					} else {
955*2d543d20SAndroid Build Coastguard Worker 						state->current->next = new_node;
956*2d543d20SAndroid Build Coastguard Worker 						state->current = new_node;
957*2d543d20SAndroid Build Coastguard Worker 					}
958*2d543d20SAndroid Build Coastguard Worker 				}
959*2d543d20SAndroid Build Coastguard Worker 			}
960*2d543d20SAndroid Build Coastguard Worker 			/* fall through */
961*2d543d20SAndroid Build Coastguard Worker 		default:
962*2d543d20SAndroid Build Coastguard Worker 			if (strlcpy(ent_path, ftsent->fts_path, sizeof(ent_path)) >= sizeof(ent_path)) {
963*2d543d20SAndroid Build Coastguard Worker 				selinux_log(SELINUX_ERROR,
964*2d543d20SAndroid Build Coastguard Worker 					    "Path name too long on %s.\n",
965*2d543d20SAndroid Build Coastguard Worker 					    ftsent->fts_path);
966*2d543d20SAndroid Build Coastguard Worker 				errno = ENAMETOOLONG;
967*2d543d20SAndroid Build Coastguard Worker 				state->error = -1;
968*2d543d20SAndroid Build Coastguard Worker 				state->abort = true;
969*2d543d20SAndroid Build Coastguard Worker 				goto finish;
970*2d543d20SAndroid Build Coastguard Worker 			}
971*2d543d20SAndroid Build Coastguard Worker 
972*2d543d20SAndroid Build Coastguard Worker 			ent_st = *ftsent->fts_statp;
973*2d543d20SAndroid Build Coastguard Worker 			if (state->parallel)
974*2d543d20SAndroid Build Coastguard Worker 				pthread_mutex_unlock(&state->mutex);
975*2d543d20SAndroid Build Coastguard Worker 
976*2d543d20SAndroid Build Coastguard Worker 			error = restorecon_sb(ent_path, &ent_st, &state->flags,
977*2d543d20SAndroid Build Coastguard Worker 					      first);
978*2d543d20SAndroid Build Coastguard Worker 
979*2d543d20SAndroid Build Coastguard Worker 			if (state->parallel) {
980*2d543d20SAndroid Build Coastguard Worker 				pthread_mutex_lock(&state->mutex);
981*2d543d20SAndroid Build Coastguard Worker 				if (state->abort)
982*2d543d20SAndroid Build Coastguard Worker 					goto unlock;
983*2d543d20SAndroid Build Coastguard Worker 			}
984*2d543d20SAndroid Build Coastguard Worker 
985*2d543d20SAndroid Build Coastguard Worker 			first = false;
986*2d543d20SAndroid Build Coastguard Worker 			if (error) {
987*2d543d20SAndroid Build Coastguard Worker 				if (state->flags.abort_on_error) {
988*2d543d20SAndroid Build Coastguard Worker 					state->error = error;
989*2d543d20SAndroid Build Coastguard Worker 					state->abort = true;
990*2d543d20SAndroid Build Coastguard Worker 					goto finish;
991*2d543d20SAndroid Build Coastguard Worker 				}
992*2d543d20SAndroid Build Coastguard Worker 				if (state->flags.count_errors)
993*2d543d20SAndroid Build Coastguard Worker 					state->skipped_errors++;
994*2d543d20SAndroid Build Coastguard Worker 				else
995*2d543d20SAndroid Build Coastguard Worker 					state->error = error;
996*2d543d20SAndroid Build Coastguard Worker 			}
997*2d543d20SAndroid Build Coastguard Worker 			break;
998*2d543d20SAndroid Build Coastguard Worker 		}
999*2d543d20SAndroid Build Coastguard Worker 	}
1000*2d543d20SAndroid Build Coastguard Worker 
1001*2d543d20SAndroid Build Coastguard Worker finish:
1002*2d543d20SAndroid Build Coastguard Worker 	if (!state->saved_errno)
1003*2d543d20SAndroid Build Coastguard Worker 		state->saved_errno = errno;
1004*2d543d20SAndroid Build Coastguard Worker unlock:
1005*2d543d20SAndroid Build Coastguard Worker 	if (state->parallel)
1006*2d543d20SAndroid Build Coastguard Worker 		pthread_mutex_unlock(&state->mutex);
1007*2d543d20SAndroid Build Coastguard Worker 	return NULL;
1008*2d543d20SAndroid Build Coastguard Worker }
1009*2d543d20SAndroid Build Coastguard Worker 
selinux_restorecon_common(const char * pathname_orig,unsigned int restorecon_flags,size_t nthreads)1010*2d543d20SAndroid Build Coastguard Worker static int selinux_restorecon_common(const char *pathname_orig,
1011*2d543d20SAndroid Build Coastguard Worker 				     unsigned int restorecon_flags,
1012*2d543d20SAndroid Build Coastguard Worker 				     size_t nthreads)
1013*2d543d20SAndroid Build Coastguard Worker {
1014*2d543d20SAndroid Build Coastguard Worker 	struct rest_state state;
1015*2d543d20SAndroid Build Coastguard Worker 
1016*2d543d20SAndroid Build Coastguard Worker 	state.flags.nochange = (restorecon_flags &
1017*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
1018*2d543d20SAndroid Build Coastguard Worker 	state.flags.verbose = (restorecon_flags &
1019*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_VERBOSE) ? true : false;
1020*2d543d20SAndroid Build Coastguard Worker 	state.flags.progress = (restorecon_flags &
1021*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_PROGRESS) ? true : false;
1022*2d543d20SAndroid Build Coastguard Worker 	state.flags.mass_relabel = (restorecon_flags &
1023*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
1024*2d543d20SAndroid Build Coastguard Worker 	state.flags.recurse = (restorecon_flags &
1025*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_RECURSE) ? true : false;
1026*2d543d20SAndroid Build Coastguard Worker 	state.flags.set_specctx = (restorecon_flags &
1027*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
1028*2d543d20SAndroid Build Coastguard Worker 	state.flags.userealpath = (restorecon_flags &
1029*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_REALPATH) ? true : false;
1030*2d543d20SAndroid Build Coastguard Worker 	state.flags.set_xdev = (restorecon_flags &
1031*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_XDEV) ? true : false;
1032*2d543d20SAndroid Build Coastguard Worker 	state.flags.add_assoc = (restorecon_flags &
1033*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
1034*2d543d20SAndroid Build Coastguard Worker 	state.flags.abort_on_error = (restorecon_flags &
1035*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
1036*2d543d20SAndroid Build Coastguard Worker 	state.flags.syslog_changes = (restorecon_flags &
1037*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
1038*2d543d20SAndroid Build Coastguard Worker 	state.flags.log_matches = (restorecon_flags &
1039*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
1040*2d543d20SAndroid Build Coastguard Worker 	state.flags.ignore_noent = (restorecon_flags &
1041*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
1042*2d543d20SAndroid Build Coastguard Worker 	state.flags.warnonnomatch = true;
1043*2d543d20SAndroid Build Coastguard Worker 	state.flags.conflicterror = (restorecon_flags &
1044*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false;
1045*2d543d20SAndroid Build Coastguard Worker 	ignore_mounts = (restorecon_flags &
1046*2d543d20SAndroid Build Coastguard Worker 		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
1047*2d543d20SAndroid Build Coastguard Worker 	state.ignore_digest = (restorecon_flags &
1048*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
1049*2d543d20SAndroid Build Coastguard Worker 	state.flags.count_errors = (restorecon_flags &
1050*2d543d20SAndroid Build Coastguard Worker 		    SELINUX_RESTORECON_COUNT_ERRORS) ? true : false;
1051*2d543d20SAndroid Build Coastguard Worker 	state.setrestorecondigest = true;
1052*2d543d20SAndroid Build Coastguard Worker 
1053*2d543d20SAndroid Build Coastguard Worker 	state.head = NULL;
1054*2d543d20SAndroid Build Coastguard Worker 	state.current = NULL;
1055*2d543d20SAndroid Build Coastguard Worker 	state.abort = false;
1056*2d543d20SAndroid Build Coastguard Worker 	state.error = 0;
1057*2d543d20SAndroid Build Coastguard Worker 	state.skipped_errors = 0;
1058*2d543d20SAndroid Build Coastguard Worker 	state.saved_errno = 0;
1059*2d543d20SAndroid Build Coastguard Worker 
1060*2d543d20SAndroid Build Coastguard Worker 	struct stat sb;
1061*2d543d20SAndroid Build Coastguard Worker 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
1062*2d543d20SAndroid Build Coastguard Worker 	char *paths[2] = { NULL, NULL };
1063*2d543d20SAndroid Build Coastguard Worker 	int fts_flags, error;
1064*2d543d20SAndroid Build Coastguard Worker 	struct dir_hash_node *current = NULL;
1065*2d543d20SAndroid Build Coastguard Worker 
1066*2d543d20SAndroid Build Coastguard Worker 	if (state.flags.verbose && state.flags.progress)
1067*2d543d20SAndroid Build Coastguard Worker 		state.flags.verbose = false;
1068*2d543d20SAndroid Build Coastguard Worker 
1069*2d543d20SAndroid Build Coastguard Worker 	__selinux_once(fc_once, restorecon_init);
1070*2d543d20SAndroid Build Coastguard Worker 
1071*2d543d20SAndroid Build Coastguard Worker 	if (!fc_sehandle)
1072*2d543d20SAndroid Build Coastguard Worker 		return -1;
1073*2d543d20SAndroid Build Coastguard Worker 
1074*2d543d20SAndroid Build Coastguard Worker 	/*
1075*2d543d20SAndroid Build Coastguard Worker 	 * If selabel_no_digest = true then no digest has been requested by
1076*2d543d20SAndroid Build Coastguard Worker 	 * an external selabel_open(3) call.
1077*2d543d20SAndroid Build Coastguard Worker 	 */
1078*2d543d20SAndroid Build Coastguard Worker 	if (selabel_no_digest ||
1079*2d543d20SAndroid Build Coastguard Worker 	    (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST))
1080*2d543d20SAndroid Build Coastguard Worker 		state.setrestorecondigest = false;
1081*2d543d20SAndroid Build Coastguard Worker 
1082*2d543d20SAndroid Build Coastguard Worker 	if (!__pthread_supported) {
1083*2d543d20SAndroid Build Coastguard Worker 		if (nthreads != 1) {
1084*2d543d20SAndroid Build Coastguard Worker 			nthreads = 1;
1085*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_WARNING,
1086*2d543d20SAndroid Build Coastguard Worker 				"Threading functionality not available, falling back to 1 thread.");
1087*2d543d20SAndroid Build Coastguard Worker 		}
1088*2d543d20SAndroid Build Coastguard Worker 	} else if (nthreads == 0) {
1089*2d543d20SAndroid Build Coastguard Worker 		long nproc = sysconf(_SC_NPROCESSORS_ONLN);
1090*2d543d20SAndroid Build Coastguard Worker 
1091*2d543d20SAndroid Build Coastguard Worker 		if (nproc > 0) {
1092*2d543d20SAndroid Build Coastguard Worker 			nthreads = nproc;
1093*2d543d20SAndroid Build Coastguard Worker 		} else {
1094*2d543d20SAndroid Build Coastguard Worker 			nthreads = 1;
1095*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_WARNING,
1096*2d543d20SAndroid Build Coastguard Worker 				"Unable to detect CPU count, falling back to 1 thread.");
1097*2d543d20SAndroid Build Coastguard Worker 		}
1098*2d543d20SAndroid Build Coastguard Worker 	}
1099*2d543d20SAndroid Build Coastguard Worker 
1100*2d543d20SAndroid Build Coastguard Worker 	/*
1101*2d543d20SAndroid Build Coastguard Worker 	 * Convert passed-in pathname to canonical pathname by resolving
1102*2d543d20SAndroid Build Coastguard Worker 	 * realpath of containing dir, then appending last component name.
1103*2d543d20SAndroid Build Coastguard Worker 	 */
1104*2d543d20SAndroid Build Coastguard Worker 	if (state.flags.userealpath) {
1105*2d543d20SAndroid Build Coastguard Worker 		char *basename_cpy = strdup(pathname_orig);
1106*2d543d20SAndroid Build Coastguard Worker 		if (!basename_cpy)
1107*2d543d20SAndroid Build Coastguard Worker 			goto realpatherr;
1108*2d543d20SAndroid Build Coastguard Worker 		pathbname = basename(basename_cpy);
1109*2d543d20SAndroid Build Coastguard Worker 		if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") ||
1110*2d543d20SAndroid Build Coastguard Worker 					    !strcmp(pathbname, "..")) {
1111*2d543d20SAndroid Build Coastguard Worker 			pathname = realpath(pathname_orig, NULL);
1112*2d543d20SAndroid Build Coastguard Worker 			if (!pathname) {
1113*2d543d20SAndroid Build Coastguard Worker 				free(basename_cpy);
1114*2d543d20SAndroid Build Coastguard Worker 				/* missing parent directory */
1115*2d543d20SAndroid Build Coastguard Worker 				if (state.flags.ignore_noent && errno == ENOENT) {
1116*2d543d20SAndroid Build Coastguard Worker 					return 0;
1117*2d543d20SAndroid Build Coastguard Worker 				}
1118*2d543d20SAndroid Build Coastguard Worker 				goto realpatherr;
1119*2d543d20SAndroid Build Coastguard Worker 			}
1120*2d543d20SAndroid Build Coastguard Worker 		} else {
1121*2d543d20SAndroid Build Coastguard Worker 			char *dirname_cpy = strdup(pathname_orig);
1122*2d543d20SAndroid Build Coastguard Worker 			if (!dirname_cpy) {
1123*2d543d20SAndroid Build Coastguard Worker 				free(basename_cpy);
1124*2d543d20SAndroid Build Coastguard Worker 				goto realpatherr;
1125*2d543d20SAndroid Build Coastguard Worker 			}
1126*2d543d20SAndroid Build Coastguard Worker 			pathdname = dirname(dirname_cpy);
1127*2d543d20SAndroid Build Coastguard Worker 			pathdnamer = realpath(pathdname, NULL);
1128*2d543d20SAndroid Build Coastguard Worker 			free(dirname_cpy);
1129*2d543d20SAndroid Build Coastguard Worker 			if (!pathdnamer) {
1130*2d543d20SAndroid Build Coastguard Worker 				free(basename_cpy);
1131*2d543d20SAndroid Build Coastguard Worker 				if (state.flags.ignore_noent && errno == ENOENT) {
1132*2d543d20SAndroid Build Coastguard Worker 					return 0;
1133*2d543d20SAndroid Build Coastguard Worker 				}
1134*2d543d20SAndroid Build Coastguard Worker 				goto realpatherr;
1135*2d543d20SAndroid Build Coastguard Worker 			}
1136*2d543d20SAndroid Build Coastguard Worker 			if (!strcmp(pathdnamer, "/"))
1137*2d543d20SAndroid Build Coastguard Worker 				error = asprintf(&pathname, "/%s", pathbname);
1138*2d543d20SAndroid Build Coastguard Worker 			else
1139*2d543d20SAndroid Build Coastguard Worker 				error = asprintf(&pathname, "%s/%s",
1140*2d543d20SAndroid Build Coastguard Worker 						    pathdnamer, pathbname);
1141*2d543d20SAndroid Build Coastguard Worker 			if (error < 0) {
1142*2d543d20SAndroid Build Coastguard Worker 				free(basename_cpy);
1143*2d543d20SAndroid Build Coastguard Worker 				goto oom;
1144*2d543d20SAndroid Build Coastguard Worker 			}
1145*2d543d20SAndroid Build Coastguard Worker 		}
1146*2d543d20SAndroid Build Coastguard Worker 		free(basename_cpy);
1147*2d543d20SAndroid Build Coastguard Worker 	} else {
1148*2d543d20SAndroid Build Coastguard Worker 		pathname = strdup(pathname_orig);
1149*2d543d20SAndroid Build Coastguard Worker 		if (!pathname)
1150*2d543d20SAndroid Build Coastguard Worker 			goto oom;
1151*2d543d20SAndroid Build Coastguard Worker 	}
1152*2d543d20SAndroid Build Coastguard Worker 
1153*2d543d20SAndroid Build Coastguard Worker 	paths[0] = pathname;
1154*2d543d20SAndroid Build Coastguard Worker 
1155*2d543d20SAndroid Build Coastguard Worker 	if (lstat(pathname, &sb) < 0) {
1156*2d543d20SAndroid Build Coastguard Worker 		if (state.flags.ignore_noent && errno == ENOENT) {
1157*2d543d20SAndroid Build Coastguard Worker 			free(pathdnamer);
1158*2d543d20SAndroid Build Coastguard Worker 			free(pathname);
1159*2d543d20SAndroid Build Coastguard Worker 			return 0;
1160*2d543d20SAndroid Build Coastguard Worker 		} else {
1161*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
1162*2d543d20SAndroid Build Coastguard Worker 				    "lstat(%s) failed: %m\n",
1163*2d543d20SAndroid Build Coastguard Worker 				    pathname);
1164*2d543d20SAndroid Build Coastguard Worker 			error = -1;
1165*2d543d20SAndroid Build Coastguard Worker 			goto cleanup;
1166*2d543d20SAndroid Build Coastguard Worker 		}
1167*2d543d20SAndroid Build Coastguard Worker 	}
1168*2d543d20SAndroid Build Coastguard Worker 
1169*2d543d20SAndroid Build Coastguard Worker 	/* Skip digest if not a directory */
1170*2d543d20SAndroid Build Coastguard Worker 	if (!S_ISDIR(sb.st_mode))
1171*2d543d20SAndroid Build Coastguard Worker 		state.setrestorecondigest = false;
1172*2d543d20SAndroid Build Coastguard Worker 
1173*2d543d20SAndroid Build Coastguard Worker 	if (!state.flags.recurse) {
1174*2d543d20SAndroid Build Coastguard Worker 		if (check_excluded(pathname)) {
1175*2d543d20SAndroid Build Coastguard Worker 			error = 0;
1176*2d543d20SAndroid Build Coastguard Worker 			goto cleanup;
1177*2d543d20SAndroid Build Coastguard Worker 		}
1178*2d543d20SAndroid Build Coastguard Worker 
1179*2d543d20SAndroid Build Coastguard Worker 		error = restorecon_sb(pathname, &sb, &state.flags, true);
1180*2d543d20SAndroid Build Coastguard Worker 		goto cleanup;
1181*2d543d20SAndroid Build Coastguard Worker 	}
1182*2d543d20SAndroid Build Coastguard Worker 
1183*2d543d20SAndroid Build Coastguard Worker 	/* Obtain fs type */
1184*2d543d20SAndroid Build Coastguard Worker 	memset(&state.sfsb, 0, sizeof(state.sfsb));
1185*2d543d20SAndroid Build Coastguard Worker 	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) {
1186*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR,
1187*2d543d20SAndroid Build Coastguard Worker 			    "statfs(%s) failed: %m\n",
1188*2d543d20SAndroid Build Coastguard Worker 			    pathname);
1189*2d543d20SAndroid Build Coastguard Worker 		error = -1;
1190*2d543d20SAndroid Build Coastguard Worker 		goto cleanup;
1191*2d543d20SAndroid Build Coastguard Worker 	}
1192*2d543d20SAndroid Build Coastguard Worker 
1193*2d543d20SAndroid Build Coastguard Worker 	/* Skip digest on in-memory filesystems and /sys */
1194*2d543d20SAndroid Build Coastguard Worker 	if ((uint32_t)state.sfsb.f_type == (uint32_t)RAMFS_MAGIC ||
1195*2d543d20SAndroid Build Coastguard Worker 		state.sfsb.f_type == TMPFS_MAGIC || state.sfsb.f_type == SYSFS_MAGIC)
1196*2d543d20SAndroid Build Coastguard Worker 		state.setrestorecondigest = false;
1197*2d543d20SAndroid Build Coastguard Worker 
1198*2d543d20SAndroid Build Coastguard Worker 	if (state.flags.set_xdev)
1199*2d543d20SAndroid Build Coastguard Worker 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
1200*2d543d20SAndroid Build Coastguard Worker 	else
1201*2d543d20SAndroid Build Coastguard Worker 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
1202*2d543d20SAndroid Build Coastguard Worker 
1203*2d543d20SAndroid Build Coastguard Worker 	state.fts = fts_open(paths, fts_flags, NULL);
1204*2d543d20SAndroid Build Coastguard Worker 	if (!state.fts)
1205*2d543d20SAndroid Build Coastguard Worker 		goto fts_err;
1206*2d543d20SAndroid Build Coastguard Worker 
1207*2d543d20SAndroid Build Coastguard Worker 	state.ftsent_first = fts_read(state.fts);
1208*2d543d20SAndroid Build Coastguard Worker 	if (!state.ftsent_first)
1209*2d543d20SAndroid Build Coastguard Worker 		goto fts_err;
1210*2d543d20SAndroid Build Coastguard Worker 
1211*2d543d20SAndroid Build Coastguard Worker 	/*
1212*2d543d20SAndroid Build Coastguard Worker 	 * Keep the inode of the first device. This is because the FTS_XDEV
1213*2d543d20SAndroid Build Coastguard Worker 	 * flag tells fts not to descend into directories with different
1214*2d543d20SAndroid Build Coastguard Worker 	 * device numbers, but fts will still give back the actual directory.
1215*2d543d20SAndroid Build Coastguard Worker 	 * By saving the device number of the directory that was passed to
1216*2d543d20SAndroid Build Coastguard Worker 	 * selinux_restorecon() and then skipping all actions on any
1217*2d543d20SAndroid Build Coastguard Worker 	 * directories with a different device number when the FTS_XDEV flag
1218*2d543d20SAndroid Build Coastguard Worker 	 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
1219*2d543d20SAndroid Build Coastguard Worker 	 */
1220*2d543d20SAndroid Build Coastguard Worker 	state.dev_num = state.ftsent_first->fts_statp->st_dev;
1221*2d543d20SAndroid Build Coastguard Worker 
1222*2d543d20SAndroid Build Coastguard Worker 	if (nthreads == 1) {
1223*2d543d20SAndroid Build Coastguard Worker 		state.parallel = false;
1224*2d543d20SAndroid Build Coastguard Worker 		selinux_restorecon_thread(&state);
1225*2d543d20SAndroid Build Coastguard Worker 	} else {
1226*2d543d20SAndroid Build Coastguard Worker 		size_t i;
1227*2d543d20SAndroid Build Coastguard Worker 		pthread_t self = pthread_self();
1228*2d543d20SAndroid Build Coastguard Worker 		pthread_t *threads = NULL;
1229*2d543d20SAndroid Build Coastguard Worker 
1230*2d543d20SAndroid Build Coastguard Worker 		pthread_mutex_init(&state.mutex, NULL);
1231*2d543d20SAndroid Build Coastguard Worker 
1232*2d543d20SAndroid Build Coastguard Worker 		threads = calloc(nthreads - 1, sizeof(*threads));
1233*2d543d20SAndroid Build Coastguard Worker 		if (!threads)
1234*2d543d20SAndroid Build Coastguard Worker 			goto oom;
1235*2d543d20SAndroid Build Coastguard Worker 
1236*2d543d20SAndroid Build Coastguard Worker 		state.parallel = true;
1237*2d543d20SAndroid Build Coastguard Worker 		/*
1238*2d543d20SAndroid Build Coastguard Worker 		 * Start (nthreads - 1) threads - the main thread is going to
1239*2d543d20SAndroid Build Coastguard Worker 		 * take part, too.
1240*2d543d20SAndroid Build Coastguard Worker 		 */
1241*2d543d20SAndroid Build Coastguard Worker 		for (i = 0; i < nthreads - 1; i++) {
1242*2d543d20SAndroid Build Coastguard Worker 			if (pthread_create(&threads[i], NULL,
1243*2d543d20SAndroid Build Coastguard Worker 					   selinux_restorecon_thread, &state)) {
1244*2d543d20SAndroid Build Coastguard Worker 				/*
1245*2d543d20SAndroid Build Coastguard Worker 				 * If any thread fails to be created, just mark
1246*2d543d20SAndroid Build Coastguard Worker 				 * it as such and let the successfully created
1247*2d543d20SAndroid Build Coastguard Worker 				 * threads do the job. In the worst case the
1248*2d543d20SAndroid Build Coastguard Worker 				 * main thread will do everything, but that's
1249*2d543d20SAndroid Build Coastguard Worker 				 * still better than to give up.
1250*2d543d20SAndroid Build Coastguard Worker 				 */
1251*2d543d20SAndroid Build Coastguard Worker 				threads[i] = self;
1252*2d543d20SAndroid Build Coastguard Worker 			}
1253*2d543d20SAndroid Build Coastguard Worker 		}
1254*2d543d20SAndroid Build Coastguard Worker 
1255*2d543d20SAndroid Build Coastguard Worker 		/* Let's join in on the fun! */
1256*2d543d20SAndroid Build Coastguard Worker 		selinux_restorecon_thread(&state);
1257*2d543d20SAndroid Build Coastguard Worker 
1258*2d543d20SAndroid Build Coastguard Worker 		/* Now wait for all threads to finish. */
1259*2d543d20SAndroid Build Coastguard Worker 		for (i = 0; i < nthreads - 1; i++) {
1260*2d543d20SAndroid Build Coastguard Worker 			/* Skip threads that failed to be created. */
1261*2d543d20SAndroid Build Coastguard Worker 			if (pthread_equal(threads[i], self))
1262*2d543d20SAndroid Build Coastguard Worker 				continue;
1263*2d543d20SAndroid Build Coastguard Worker 			pthread_join(threads[i], NULL);
1264*2d543d20SAndroid Build Coastguard Worker 		}
1265*2d543d20SAndroid Build Coastguard Worker 		free(threads);
1266*2d543d20SAndroid Build Coastguard Worker 
1267*2d543d20SAndroid Build Coastguard Worker 		pthread_mutex_destroy(&state.mutex);
1268*2d543d20SAndroid Build Coastguard Worker 	}
1269*2d543d20SAndroid Build Coastguard Worker 
1270*2d543d20SAndroid Build Coastguard Worker 	error = state.error;
1271*2d543d20SAndroid Build Coastguard Worker 	if (state.saved_errno)
1272*2d543d20SAndroid Build Coastguard Worker 		goto out;
1273*2d543d20SAndroid Build Coastguard Worker 
1274*2d543d20SAndroid Build Coastguard Worker 	/*
1275*2d543d20SAndroid Build Coastguard Worker 	 * Labeling successful. Write partial match digests for subdirectories.
1276*2d543d20SAndroid Build Coastguard Worker 	 * TODO: Write digest upon FTS_DP if no error occurs in its descents.
1277*2d543d20SAndroid Build Coastguard Worker 	 * Note: we can't ignore errors here that we've masked due to
1278*2d543d20SAndroid Build Coastguard Worker 	 * SELINUX_RESTORECON_COUNT_ERRORS.
1279*2d543d20SAndroid Build Coastguard Worker 	 */
1280*2d543d20SAndroid Build Coastguard Worker 	if (state.setrestorecondigest && !state.flags.nochange && !error &&
1281*2d543d20SAndroid Build Coastguard Worker 	    state.skipped_errors == 0) {
1282*2d543d20SAndroid Build Coastguard Worker 		current = state.head;
1283*2d543d20SAndroid Build Coastguard Worker 		while (current != NULL) {
1284*2d543d20SAndroid Build Coastguard Worker 			if (setxattr(current->path,
1285*2d543d20SAndroid Build Coastguard Worker 			    RESTORECON_PARTIAL_MATCH_DIGEST,
1286*2d543d20SAndroid Build Coastguard Worker 			    current->digest,
1287*2d543d20SAndroid Build Coastguard Worker 			    SHA1_HASH_SIZE, 0) < 0) {
1288*2d543d20SAndroid Build Coastguard Worker 				selinux_log(SELINUX_ERROR,
1289*2d543d20SAndroid Build Coastguard Worker 					    "setxattr failed: %s: %m\n",
1290*2d543d20SAndroid Build Coastguard Worker 					    current->path);
1291*2d543d20SAndroid Build Coastguard Worker 			}
1292*2d543d20SAndroid Build Coastguard Worker 			current = current->next;
1293*2d543d20SAndroid Build Coastguard Worker 		}
1294*2d543d20SAndroid Build Coastguard Worker 	}
1295*2d543d20SAndroid Build Coastguard Worker 
1296*2d543d20SAndroid Build Coastguard Worker 	skipped_errors = state.skipped_errors;
1297*2d543d20SAndroid Build Coastguard Worker 
1298*2d543d20SAndroid Build Coastguard Worker out:
1299*2d543d20SAndroid Build Coastguard Worker 	if (state.flags.progress && state.flags.mass_relabel)
1300*2d543d20SAndroid Build Coastguard Worker 		fprintf(stdout, "\r%s 100.0%%\n", pathname);
1301*2d543d20SAndroid Build Coastguard Worker 
1302*2d543d20SAndroid Build Coastguard Worker 	(void) fts_close(state.fts);
1303*2d543d20SAndroid Build Coastguard Worker 	errno = state.saved_errno;
1304*2d543d20SAndroid Build Coastguard Worker cleanup:
1305*2d543d20SAndroid Build Coastguard Worker 	if (state.flags.add_assoc) {
1306*2d543d20SAndroid Build Coastguard Worker 		if (state.flags.verbose)
1307*2d543d20SAndroid Build Coastguard Worker 			filespec_eval();
1308*2d543d20SAndroid Build Coastguard Worker 		filespec_destroy();
1309*2d543d20SAndroid Build Coastguard Worker 	}
1310*2d543d20SAndroid Build Coastguard Worker 	free(pathdnamer);
1311*2d543d20SAndroid Build Coastguard Worker 	free(pathname);
1312*2d543d20SAndroid Build Coastguard Worker 
1313*2d543d20SAndroid Build Coastguard Worker 	current = state.head;
1314*2d543d20SAndroid Build Coastguard Worker 	while (current != NULL) {
1315*2d543d20SAndroid Build Coastguard Worker 		struct dir_hash_node *next = current->next;
1316*2d543d20SAndroid Build Coastguard Worker 
1317*2d543d20SAndroid Build Coastguard Worker 		free(current->path);
1318*2d543d20SAndroid Build Coastguard Worker 		free(current);
1319*2d543d20SAndroid Build Coastguard Worker 		current = next;
1320*2d543d20SAndroid Build Coastguard Worker 	}
1321*2d543d20SAndroid Build Coastguard Worker 	return error;
1322*2d543d20SAndroid Build Coastguard Worker 
1323*2d543d20SAndroid Build Coastguard Worker oom:
1324*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
1325*2d543d20SAndroid Build Coastguard Worker 	error = -1;
1326*2d543d20SAndroid Build Coastguard Worker 	goto cleanup;
1327*2d543d20SAndroid Build Coastguard Worker 
1328*2d543d20SAndroid Build Coastguard Worker realpatherr:
1329*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR,
1330*2d543d20SAndroid Build Coastguard Worker 		    "SELinux: Could not get canonical path for %s restorecon: %m.\n",
1331*2d543d20SAndroid Build Coastguard Worker 		    pathname_orig);
1332*2d543d20SAndroid Build Coastguard Worker 	error = -1;
1333*2d543d20SAndroid Build Coastguard Worker 	goto cleanup;
1334*2d543d20SAndroid Build Coastguard Worker 
1335*2d543d20SAndroid Build Coastguard Worker fts_err:
1336*2d543d20SAndroid Build Coastguard Worker 	selinux_log(SELINUX_ERROR,
1337*2d543d20SAndroid Build Coastguard Worker 		    "fts error while labeling %s: %m\n",
1338*2d543d20SAndroid Build Coastguard Worker 		    paths[0]);
1339*2d543d20SAndroid Build Coastguard Worker 	error = -1;
1340*2d543d20SAndroid Build Coastguard Worker 	goto cleanup;
1341*2d543d20SAndroid Build Coastguard Worker }
1342*2d543d20SAndroid Build Coastguard Worker 
1343*2d543d20SAndroid Build Coastguard Worker 
1344*2d543d20SAndroid Build Coastguard Worker /*
1345*2d543d20SAndroid Build Coastguard Worker  * Public API
1346*2d543d20SAndroid Build Coastguard Worker  */
1347*2d543d20SAndroid Build Coastguard Worker 
1348*2d543d20SAndroid Build Coastguard Worker /* selinux_restorecon(3) - Main function that is responsible for labeling */
selinux_restorecon(const char * pathname_orig,unsigned int restorecon_flags)1349*2d543d20SAndroid Build Coastguard Worker int selinux_restorecon(const char *pathname_orig,
1350*2d543d20SAndroid Build Coastguard Worker 		       unsigned int restorecon_flags)
1351*2d543d20SAndroid Build Coastguard Worker {
1352*2d543d20SAndroid Build Coastguard Worker 	return selinux_restorecon_common(pathname_orig, restorecon_flags, 1);
1353*2d543d20SAndroid Build Coastguard Worker }
1354*2d543d20SAndroid Build Coastguard Worker 
1355*2d543d20SAndroid Build Coastguard Worker /* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */
selinux_restorecon_parallel(const char * pathname_orig,unsigned int restorecon_flags,size_t nthreads)1356*2d543d20SAndroid Build Coastguard Worker int selinux_restorecon_parallel(const char *pathname_orig,
1357*2d543d20SAndroid Build Coastguard Worker 				unsigned int restorecon_flags,
1358*2d543d20SAndroid Build Coastguard Worker 				size_t nthreads)
1359*2d543d20SAndroid Build Coastguard Worker {
1360*2d543d20SAndroid Build Coastguard Worker 	return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads);
1361*2d543d20SAndroid Build Coastguard Worker }
1362*2d543d20SAndroid Build Coastguard Worker 
1363*2d543d20SAndroid Build Coastguard Worker /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
selinux_restorecon_set_sehandle(struct selabel_handle * hndl)1364*2d543d20SAndroid Build Coastguard Worker void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
1365*2d543d20SAndroid Build Coastguard Worker {
1366*2d543d20SAndroid Build Coastguard Worker 	char **specfiles;
1367*2d543d20SAndroid Build Coastguard Worker 	unsigned char *fc_digest;
1368*2d543d20SAndroid Build Coastguard Worker 	size_t num_specfiles, fc_digest_len;
1369*2d543d20SAndroid Build Coastguard Worker 
1370*2d543d20SAndroid Build Coastguard Worker 	fc_sehandle = hndl;
1371*2d543d20SAndroid Build Coastguard Worker 	if (!fc_sehandle)
1372*2d543d20SAndroid Build Coastguard Worker 		return;
1373*2d543d20SAndroid Build Coastguard Worker 
1374*2d543d20SAndroid Build Coastguard Worker 	/* Check if digest requested in selabel_open(3), if so use it. */
1375*2d543d20SAndroid Build Coastguard Worker 	if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
1376*2d543d20SAndroid Build Coastguard Worker 				   &specfiles, &num_specfiles) < 0)
1377*2d543d20SAndroid Build Coastguard Worker 		selabel_no_digest = true;
1378*2d543d20SAndroid Build Coastguard Worker 	else
1379*2d543d20SAndroid Build Coastguard Worker 		selabel_no_digest = false;
1380*2d543d20SAndroid Build Coastguard Worker }
1381*2d543d20SAndroid Build Coastguard Worker 
1382*2d543d20SAndroid Build Coastguard Worker 
1383*2d543d20SAndroid Build Coastguard Worker /*
1384*2d543d20SAndroid Build Coastguard Worker  * selinux_restorecon_default_handle(3) is called to set the global restorecon
1385*2d543d20SAndroid Build Coastguard Worker  * handle by a process if the default params are required.
1386*2d543d20SAndroid Build Coastguard Worker  */
selinux_restorecon_default_handle(void)1387*2d543d20SAndroid Build Coastguard Worker struct selabel_handle *selinux_restorecon_default_handle(void)
1388*2d543d20SAndroid Build Coastguard Worker {
1389*2d543d20SAndroid Build Coastguard Worker 	struct selabel_handle *sehandle;
1390*2d543d20SAndroid Build Coastguard Worker 
1391*2d543d20SAndroid Build Coastguard Worker 	struct selinux_opt fc_opts[] = {
1392*2d543d20SAndroid Build Coastguard Worker 		{ SELABEL_OPT_DIGEST, (char *)1 }
1393*2d543d20SAndroid Build Coastguard Worker 	};
1394*2d543d20SAndroid Build Coastguard Worker 
1395*2d543d20SAndroid Build Coastguard Worker 	sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
1396*2d543d20SAndroid Build Coastguard Worker 
1397*2d543d20SAndroid Build Coastguard Worker 	if (!sehandle) {
1398*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR,
1399*2d543d20SAndroid Build Coastguard Worker 			    "Error obtaining file context handle: %m\n");
1400*2d543d20SAndroid Build Coastguard Worker 		return NULL;
1401*2d543d20SAndroid Build Coastguard Worker 	}
1402*2d543d20SAndroid Build Coastguard Worker 
1403*2d543d20SAndroid Build Coastguard Worker 	selabel_no_digest = false;
1404*2d543d20SAndroid Build Coastguard Worker 	return sehandle;
1405*2d543d20SAndroid Build Coastguard Worker }
1406*2d543d20SAndroid Build Coastguard Worker 
1407*2d543d20SAndroid Build Coastguard Worker /*
1408*2d543d20SAndroid Build Coastguard Worker  * selinux_restorecon_set_exclude_list(3) is called to add additional entries
1409*2d543d20SAndroid Build Coastguard Worker  * to be excluded from labeling checks.
1410*2d543d20SAndroid Build Coastguard Worker  */
selinux_restorecon_set_exclude_list(const char ** exclude_list)1411*2d543d20SAndroid Build Coastguard Worker void selinux_restorecon_set_exclude_list(const char **exclude_list)
1412*2d543d20SAndroid Build Coastguard Worker {
1413*2d543d20SAndroid Build Coastguard Worker 	int i;
1414*2d543d20SAndroid Build Coastguard Worker 	struct stat sb;
1415*2d543d20SAndroid Build Coastguard Worker 
1416*2d543d20SAndroid Build Coastguard Worker 	for (i = 0; exclude_list[i]; i++) {
1417*2d543d20SAndroid Build Coastguard Worker 		if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) {
1418*2d543d20SAndroid Build Coastguard Worker 			selinux_log(SELINUX_ERROR,
1419*2d543d20SAndroid Build Coastguard Worker 				    "lstat error on exclude path \"%s\", %m - ignoring.\n",
1420*2d543d20SAndroid Build Coastguard Worker 				    exclude_list[i]);
1421*2d543d20SAndroid Build Coastguard Worker 			break;
1422*2d543d20SAndroid Build Coastguard Worker 		}
1423*2d543d20SAndroid Build Coastguard Worker 		if (add_exclude(exclude_list[i], CALLER_EXCLUDED) &&
1424*2d543d20SAndroid Build Coastguard Worker 		    errno == ENOMEM)
1425*2d543d20SAndroid Build Coastguard Worker 			assert(0);
1426*2d543d20SAndroid Build Coastguard Worker 	}
1427*2d543d20SAndroid Build Coastguard Worker }
1428*2d543d20SAndroid Build Coastguard Worker 
1429*2d543d20SAndroid Build Coastguard Worker /* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
selinux_restorecon_set_alt_rootpath(const char * alt_rootpath)1430*2d543d20SAndroid Build Coastguard Worker int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
1431*2d543d20SAndroid Build Coastguard Worker {
1432*2d543d20SAndroid Build Coastguard Worker 	size_t len;
1433*2d543d20SAndroid Build Coastguard Worker 
1434*2d543d20SAndroid Build Coastguard Worker 	/* This should be NULL on first use */
1435*2d543d20SAndroid Build Coastguard Worker 	if (rootpath)
1436*2d543d20SAndroid Build Coastguard Worker 		free(rootpath);
1437*2d543d20SAndroid Build Coastguard Worker 
1438*2d543d20SAndroid Build Coastguard Worker 	rootpath = strdup(alt_rootpath);
1439*2d543d20SAndroid Build Coastguard Worker 	if (!rootpath) {
1440*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
1441*2d543d20SAndroid Build Coastguard Worker 		return -1;
1442*2d543d20SAndroid Build Coastguard Worker 	}
1443*2d543d20SAndroid Build Coastguard Worker 
1444*2d543d20SAndroid Build Coastguard Worker 	/* trim trailing /, if present */
1445*2d543d20SAndroid Build Coastguard Worker 	len = strlen(rootpath);
1446*2d543d20SAndroid Build Coastguard Worker 	while (len && (rootpath[len - 1] == '/'))
1447*2d543d20SAndroid Build Coastguard Worker 		rootpath[--len] = '\0';
1448*2d543d20SAndroid Build Coastguard Worker 	rootpathlen = len;
1449*2d543d20SAndroid Build Coastguard Worker 
1450*2d543d20SAndroid Build Coastguard Worker 	return 0;
1451*2d543d20SAndroid Build Coastguard Worker }
1452*2d543d20SAndroid Build Coastguard Worker 
1453*2d543d20SAndroid Build Coastguard Worker /* selinux_restorecon_xattr(3)
1454*2d543d20SAndroid Build Coastguard Worker  * Find RESTORECON_PARTIAL_MATCH_DIGEST entries.
1455*2d543d20SAndroid Build Coastguard Worker  */
selinux_restorecon_xattr(const char * pathname,unsigned int xattr_flags,struct dir_xattr *** xattr_list)1456*2d543d20SAndroid Build Coastguard Worker int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
1457*2d543d20SAndroid Build Coastguard Worker 			     struct dir_xattr ***xattr_list)
1458*2d543d20SAndroid Build Coastguard Worker {
1459*2d543d20SAndroid Build Coastguard Worker 	bool recurse = (xattr_flags &
1460*2d543d20SAndroid Build Coastguard Worker 	    SELINUX_RESTORECON_XATTR_RECURSE) ? true : false;
1461*2d543d20SAndroid Build Coastguard Worker 	bool delete_nonmatch = (xattr_flags &
1462*2d543d20SAndroid Build Coastguard Worker 	    SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false;
1463*2d543d20SAndroid Build Coastguard Worker 	bool delete_all = (xattr_flags &
1464*2d543d20SAndroid Build Coastguard Worker 	    SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false;
1465*2d543d20SAndroid Build Coastguard Worker 	ignore_mounts = (xattr_flags &
1466*2d543d20SAndroid Build Coastguard Worker 	   SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false;
1467*2d543d20SAndroid Build Coastguard Worker 
1468*2d543d20SAndroid Build Coastguard Worker 	int rc, fts_flags;
1469*2d543d20SAndroid Build Coastguard Worker 	struct stat sb;
1470*2d543d20SAndroid Build Coastguard Worker 	struct statfs sfsb;
1471*2d543d20SAndroid Build Coastguard Worker 	struct dir_xattr *current, *next;
1472*2d543d20SAndroid Build Coastguard Worker 	FTS *fts;
1473*2d543d20SAndroid Build Coastguard Worker 	FTSENT *ftsent;
1474*2d543d20SAndroid Build Coastguard Worker 	char *paths[2] = { NULL, NULL };
1475*2d543d20SAndroid Build Coastguard Worker 
1476*2d543d20SAndroid Build Coastguard Worker 	__selinux_once(fc_once, restorecon_init);
1477*2d543d20SAndroid Build Coastguard Worker 
1478*2d543d20SAndroid Build Coastguard Worker 	if (!fc_sehandle)
1479*2d543d20SAndroid Build Coastguard Worker 		return -1;
1480*2d543d20SAndroid Build Coastguard Worker 
1481*2d543d20SAndroid Build Coastguard Worker 	if (lstat(pathname, &sb) < 0) {
1482*2d543d20SAndroid Build Coastguard Worker 		if (errno == ENOENT)
1483*2d543d20SAndroid Build Coastguard Worker 			return 0;
1484*2d543d20SAndroid Build Coastguard Worker 
1485*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR,
1486*2d543d20SAndroid Build Coastguard Worker 			    "lstat(%s) failed: %m\n",
1487*2d543d20SAndroid Build Coastguard Worker 			    pathname);
1488*2d543d20SAndroid Build Coastguard Worker 		return -1;
1489*2d543d20SAndroid Build Coastguard Worker 	}
1490*2d543d20SAndroid Build Coastguard Worker 
1491*2d543d20SAndroid Build Coastguard Worker 	if (!recurse) {
1492*2d543d20SAndroid Build Coastguard Worker 		if (statfs(pathname, &sfsb) == 0) {
1493*2d543d20SAndroid Build Coastguard Worker 			if ((uint32_t)sfsb.f_type == (uint32_t)RAMFS_MAGIC ||
1494*2d543d20SAndroid Build Coastguard Worker 			    sfsb.f_type == TMPFS_MAGIC)
1495*2d543d20SAndroid Build Coastguard Worker 				return 0;
1496*2d543d20SAndroid Build Coastguard Worker 		}
1497*2d543d20SAndroid Build Coastguard Worker 
1498*2d543d20SAndroid Build Coastguard Worker 		if (check_excluded(pathname))
1499*2d543d20SAndroid Build Coastguard Worker 			return 0;
1500*2d543d20SAndroid Build Coastguard Worker 
1501*2d543d20SAndroid Build Coastguard Worker 		rc = add_xattr_entry(pathname, delete_nonmatch, delete_all);
1502*2d543d20SAndroid Build Coastguard Worker 
1503*2d543d20SAndroid Build Coastguard Worker 		if (!rc && dir_xattr_list)
1504*2d543d20SAndroid Build Coastguard Worker 			*xattr_list = &dir_xattr_list;
1505*2d543d20SAndroid Build Coastguard Worker 		else if (rc == -1)
1506*2d543d20SAndroid Build Coastguard Worker 			return rc;
1507*2d543d20SAndroid Build Coastguard Worker 
1508*2d543d20SAndroid Build Coastguard Worker 		return 0;
1509*2d543d20SAndroid Build Coastguard Worker 	}
1510*2d543d20SAndroid Build Coastguard Worker 
1511*2d543d20SAndroid Build Coastguard Worker 	paths[0] = (char *)pathname;
1512*2d543d20SAndroid Build Coastguard Worker 	fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
1513*2d543d20SAndroid Build Coastguard Worker 
1514*2d543d20SAndroid Build Coastguard Worker 	fts = fts_open(paths, fts_flags, NULL);
1515*2d543d20SAndroid Build Coastguard Worker 	if (!fts) {
1516*2d543d20SAndroid Build Coastguard Worker 		selinux_log(SELINUX_ERROR,
1517*2d543d20SAndroid Build Coastguard Worker 			    "fts error on %s: %m\n",
1518*2d543d20SAndroid Build Coastguard Worker 			    paths[0]);
1519*2d543d20SAndroid Build Coastguard Worker 		return -1;
1520*2d543d20SAndroid Build Coastguard Worker 	}
1521*2d543d20SAndroid Build Coastguard Worker 
1522*2d543d20SAndroid Build Coastguard Worker 	while ((ftsent = fts_read(fts)) != NULL) {
1523*2d543d20SAndroid Build Coastguard Worker 		switch (ftsent->fts_info) {
1524*2d543d20SAndroid Build Coastguard Worker 		case FTS_DP:
1525*2d543d20SAndroid Build Coastguard Worker 			continue;
1526*2d543d20SAndroid Build Coastguard Worker 		case FTS_D:
1527*2d543d20SAndroid Build Coastguard Worker 			if (statfs(ftsent->fts_path, &sfsb) == 0) {
1528*2d543d20SAndroid Build Coastguard Worker 				if ((uint32_t)sfsb.f_type == (uint32_t)RAMFS_MAGIC ||
1529*2d543d20SAndroid Build Coastguard Worker 				    sfsb.f_type == TMPFS_MAGIC)
1530*2d543d20SAndroid Build Coastguard Worker 					continue;
1531*2d543d20SAndroid Build Coastguard Worker 			}
1532*2d543d20SAndroid Build Coastguard Worker 			if (check_excluded(ftsent->fts_path)) {
1533*2d543d20SAndroid Build Coastguard Worker 				fts_set(fts, ftsent, FTS_SKIP);
1534*2d543d20SAndroid Build Coastguard Worker 				continue;
1535*2d543d20SAndroid Build Coastguard Worker 			}
1536*2d543d20SAndroid Build Coastguard Worker 
1537*2d543d20SAndroid Build Coastguard Worker 			rc = add_xattr_entry(ftsent->fts_path,
1538*2d543d20SAndroid Build Coastguard Worker 					     delete_nonmatch, delete_all);
1539*2d543d20SAndroid Build Coastguard Worker 			if (rc == 1)
1540*2d543d20SAndroid Build Coastguard Worker 				continue;
1541*2d543d20SAndroid Build Coastguard Worker 			else if (rc == -1)
1542*2d543d20SAndroid Build Coastguard Worker 				goto cleanup;
1543*2d543d20SAndroid Build Coastguard Worker 			break;
1544*2d543d20SAndroid Build Coastguard Worker 		default:
1545*2d543d20SAndroid Build Coastguard Worker 			break;
1546*2d543d20SAndroid Build Coastguard Worker 		}
1547*2d543d20SAndroid Build Coastguard Worker 	}
1548*2d543d20SAndroid Build Coastguard Worker 
1549*2d543d20SAndroid Build Coastguard Worker 	if (dir_xattr_list)
1550*2d543d20SAndroid Build Coastguard Worker 		*xattr_list = &dir_xattr_list;
1551*2d543d20SAndroid Build Coastguard Worker 
1552*2d543d20SAndroid Build Coastguard Worker 	(void) fts_close(fts);
1553*2d543d20SAndroid Build Coastguard Worker 	return 0;
1554*2d543d20SAndroid Build Coastguard Worker 
1555*2d543d20SAndroid Build Coastguard Worker cleanup:
1556*2d543d20SAndroid Build Coastguard Worker 	rc = errno;
1557*2d543d20SAndroid Build Coastguard Worker 	(void) fts_close(fts);
1558*2d543d20SAndroid Build Coastguard Worker 	errno = rc;
1559*2d543d20SAndroid Build Coastguard Worker 
1560*2d543d20SAndroid Build Coastguard Worker 	if (dir_xattr_list) {
1561*2d543d20SAndroid Build Coastguard Worker 		/* Free any used memory */
1562*2d543d20SAndroid Build Coastguard Worker 		current = dir_xattr_list;
1563*2d543d20SAndroid Build Coastguard Worker 		while (current) {
1564*2d543d20SAndroid Build Coastguard Worker 			next = current->next;
1565*2d543d20SAndroid Build Coastguard Worker 			free(current->directory);
1566*2d543d20SAndroid Build Coastguard Worker 			free(current->digest);
1567*2d543d20SAndroid Build Coastguard Worker 			free(current);
1568*2d543d20SAndroid Build Coastguard Worker 			current = next;
1569*2d543d20SAndroid Build Coastguard Worker 		}
1570*2d543d20SAndroid Build Coastguard Worker 	}
1571*2d543d20SAndroid Build Coastguard Worker 	return -1;
1572*2d543d20SAndroid Build Coastguard Worker }
1573*2d543d20SAndroid Build Coastguard Worker 
selinux_restorecon_get_skipped_errors(void)1574*2d543d20SAndroid Build Coastguard Worker long unsigned selinux_restorecon_get_skipped_errors(void)
1575*2d543d20SAndroid Build Coastguard Worker {
1576*2d543d20SAndroid Build Coastguard Worker 	return skipped_errors;
1577*2d543d20SAndroid Build Coastguard Worker }
1578