16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * The majority of this code is from Android's
36cd6a6acSopenharmony_ci * external/libselinux/src/android.c and upstream
46cd6a6acSopenharmony_ci * selinux/policycoreutils/setfiles/restore.c
56cd6a6acSopenharmony_ci *
66cd6a6acSopenharmony_ci * See selinux_restorecon(3) for details.
76cd6a6acSopenharmony_ci */
86cd6a6acSopenharmony_ci
96cd6a6acSopenharmony_ci#include <unistd.h>
106cd6a6acSopenharmony_ci#include <string.h>
116cd6a6acSopenharmony_ci#include <stdio.h>
126cd6a6acSopenharmony_ci#include <stdlib.h>
136cd6a6acSopenharmony_ci#include <stdbool.h>
146cd6a6acSopenharmony_ci#include <ctype.h>
156cd6a6acSopenharmony_ci#include <errno.h>
166cd6a6acSopenharmony_ci#include <fcntl.h>
176cd6a6acSopenharmony_ci#include <include/fts.h>
186cd6a6acSopenharmony_ci#include <inttypes.h>
196cd6a6acSopenharmony_ci#include <limits.h>
206cd6a6acSopenharmony_ci#include <stdint.h>
216cd6a6acSopenharmony_ci#include <sys/types.h>
226cd6a6acSopenharmony_ci#include <sys/stat.h>
236cd6a6acSopenharmony_ci#include <sys/xattr.h>
246cd6a6acSopenharmony_ci#include <sys/vfs.h>
256cd6a6acSopenharmony_ci#include <sys/statvfs.h>
266cd6a6acSopenharmony_ci#include <sys/utsname.h>
276cd6a6acSopenharmony_ci#include <linux/magic.h>
286cd6a6acSopenharmony_ci#include <libgen.h>
296cd6a6acSopenharmony_ci#include <syslog.h>
306cd6a6acSopenharmony_ci#include <assert.h>
316cd6a6acSopenharmony_ci
326cd6a6acSopenharmony_ci#include <selinux/selinux.h>
336cd6a6acSopenharmony_ci#include <selinux/context.h>
346cd6a6acSopenharmony_ci#include <selinux/label.h>
356cd6a6acSopenharmony_ci#include <selinux/restorecon.h>
366cd6a6acSopenharmony_ci#include <selinux/skip_elx_constants.h>
376cd6a6acSopenharmony_ci
386cd6a6acSopenharmony_ci#include "ignore_path.h"
396cd6a6acSopenharmony_ci#include "callbacks.h"
406cd6a6acSopenharmony_ci#include "selinux_internal.h"
416cd6a6acSopenharmony_ci#include "label_file.h"
426cd6a6acSopenharmony_ci#include "sha1.h"
436cd6a6acSopenharmony_ci
446cd6a6acSopenharmony_ci#define STAR_COUNT 1024
456cd6a6acSopenharmony_ci
466cd6a6acSopenharmony_cistatic struct selabel_handle *fc_sehandle = NULL;
476cd6a6acSopenharmony_cistatic bool selabel_no_digest;
486cd6a6acSopenharmony_cistatic char *rootpath = NULL;
496cd6a6acSopenharmony_cistatic size_t rootpathlen;
506cd6a6acSopenharmony_ci
516cd6a6acSopenharmony_ci/* Information on excluded fs and directories. */
526cd6a6acSopenharmony_cistruct edir {
536cd6a6acSopenharmony_ci	char *directory;
546cd6a6acSopenharmony_ci	size_t size;
556cd6a6acSopenharmony_ci	/* True if excluded by selinux_restorecon_set_exclude_list(3). */
566cd6a6acSopenharmony_ci	bool caller_excluded;
576cd6a6acSopenharmony_ci};
586cd6a6acSopenharmony_ci#define CALLER_EXCLUDED true
596cd6a6acSopenharmony_cistatic bool ignore_mounts;
606cd6a6acSopenharmony_cistatic uint64_t exclude_non_seclabel_mounts(void);
616cd6a6acSopenharmony_cistatic int exclude_count = 0;
626cd6a6acSopenharmony_cistatic struct edir *exclude_lst = NULL;
636cd6a6acSopenharmony_cistatic uint64_t fc_count = 0;	/* Number of files processed so far */
646cd6a6acSopenharmony_cistatic uint64_t efile_count;	/* Estimated total number of files */
656cd6a6acSopenharmony_cistatic pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
666cd6a6acSopenharmony_ci
676cd6a6acSopenharmony_ci/* Store information on directories with xattr's. */
686cd6a6acSopenharmony_cistatic struct dir_xattr *dir_xattr_list;
696cd6a6acSopenharmony_cistatic struct dir_xattr *dir_xattr_last;
706cd6a6acSopenharmony_ci
716cd6a6acSopenharmony_ci/* Number of errors ignored during the file tree walk. */
726cd6a6acSopenharmony_cistatic long unsigned skipped_errors;
736cd6a6acSopenharmony_ci
746cd6a6acSopenharmony_ci/* restorecon_flags for passing to restorecon_sb() */
756cd6a6acSopenharmony_cistruct rest_flags {
766cd6a6acSopenharmony_ci	bool nochange;
776cd6a6acSopenharmony_ci	bool verbose;
786cd6a6acSopenharmony_ci	bool progress;
796cd6a6acSopenharmony_ci	bool mass_relabel;
806cd6a6acSopenharmony_ci	bool set_specctx;
816cd6a6acSopenharmony_ci	bool add_assoc;
826cd6a6acSopenharmony_ci	bool recurse;
836cd6a6acSopenharmony_ci	bool userealpath;
846cd6a6acSopenharmony_ci	bool set_xdev;
856cd6a6acSopenharmony_ci	bool abort_on_error;
866cd6a6acSopenharmony_ci	bool syslog_changes;
876cd6a6acSopenharmony_ci	bool log_matches;
886cd6a6acSopenharmony_ci	bool ignore_noent;
896cd6a6acSopenharmony_ci	bool warnonnomatch;
906cd6a6acSopenharmony_ci	bool conflicterror;
916cd6a6acSopenharmony_ci	bool count_errors;
926cd6a6acSopenharmony_ci	bool skipelx;
936cd6a6acSopenharmony_ci};
946cd6a6acSopenharmony_ci
956cd6a6acSopenharmony_cistatic void restorecon_init(void)
966cd6a6acSopenharmony_ci{
976cd6a6acSopenharmony_ci	struct selabel_handle *sehandle = NULL;
986cd6a6acSopenharmony_ci
996cd6a6acSopenharmony_ci	if (!fc_sehandle) {
1006cd6a6acSopenharmony_ci		sehandle = selinux_restorecon_default_handle();
1016cd6a6acSopenharmony_ci		selinux_restorecon_set_sehandle(sehandle);
1026cd6a6acSopenharmony_ci	}
1036cd6a6acSopenharmony_ci
1046cd6a6acSopenharmony_ci	efile_count = 0;
1056cd6a6acSopenharmony_ci	if (!ignore_mounts)
1066cd6a6acSopenharmony_ci		efile_count = exclude_non_seclabel_mounts();
1076cd6a6acSopenharmony_ci}
1086cd6a6acSopenharmony_ci
1096cd6a6acSopenharmony_cistatic pthread_once_t fc_once = PTHREAD_ONCE_INIT;
1106cd6a6acSopenharmony_ci
1116cd6a6acSopenharmony_ci/*
1126cd6a6acSopenharmony_ci * Manage excluded directories:
1136cd6a6acSopenharmony_ci *  remove_exclude() - This removes any conflicting entries as there could be
1146cd6a6acSopenharmony_ci *                     a case where a non-seclabel fs is mounted on /foo and
1156cd6a6acSopenharmony_ci *                     then a seclabel fs is mounted on top of it.
1166cd6a6acSopenharmony_ci *                     However if an entry has been added via
1176cd6a6acSopenharmony_ci *                     selinux_restorecon_set_exclude_list(3) do not remove.
1186cd6a6acSopenharmony_ci *
1196cd6a6acSopenharmony_ci *  add_exclude()    - Add a directory/fs to be excluded from labeling. If it
1206cd6a6acSopenharmony_ci *                     has already been added, then ignore.
1216cd6a6acSopenharmony_ci *
1226cd6a6acSopenharmony_ci *  check_excluded() - Check if directory/fs is to be excluded when relabeling.
1236cd6a6acSopenharmony_ci *
1246cd6a6acSopenharmony_ci *  file_system_count() - Calculates the number of files to be processed.
1256cd6a6acSopenharmony_ci *                        The count is only used if SELINUX_RESTORECON_PROGRESS
1266cd6a6acSopenharmony_ci *                        is set and a mass relabel is requested.
1276cd6a6acSopenharmony_ci *
1286cd6a6acSopenharmony_ci *  exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what
1296cd6a6acSopenharmony_ci *                                  non-seclabel mounts to exclude from
1306cd6a6acSopenharmony_ci *                                  relabeling. restorecon_init() will not
1316cd6a6acSopenharmony_ci *                                  call this function if the
1326cd6a6acSopenharmony_ci *                                  SELINUX_RESTORECON_IGNORE_MOUNTS
1336cd6a6acSopenharmony_ci *                                  flag is set.
1346cd6a6acSopenharmony_ci *                                  Setting SELINUX_RESTORECON_IGNORE_MOUNTS
1356cd6a6acSopenharmony_ci *                                  is useful where there is a non-seclabel fs
1366cd6a6acSopenharmony_ci *                                  mounted on /foo and then a seclabel fs is
1376cd6a6acSopenharmony_ci *                                  mounted on a directory below this.
1386cd6a6acSopenharmony_ci */
1396cd6a6acSopenharmony_cistatic void remove_exclude(const char *directory)
1406cd6a6acSopenharmony_ci{
1416cd6a6acSopenharmony_ci	int i;
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_ci	for (i = 0; i < exclude_count; i++) {
1446cd6a6acSopenharmony_ci		if (strcmp(directory, exclude_lst[i].directory) == 0 &&
1456cd6a6acSopenharmony_ci					!exclude_lst[i].caller_excluded) {
1466cd6a6acSopenharmony_ci			free(exclude_lst[i].directory);
1476cd6a6acSopenharmony_ci			if (i != exclude_count - 1)
1486cd6a6acSopenharmony_ci				exclude_lst[i] = exclude_lst[exclude_count - 1];
1496cd6a6acSopenharmony_ci			exclude_count--;
1506cd6a6acSopenharmony_ci			return;
1516cd6a6acSopenharmony_ci		}
1526cd6a6acSopenharmony_ci	}
1536cd6a6acSopenharmony_ci}
1546cd6a6acSopenharmony_ci
1556cd6a6acSopenharmony_cistatic int add_exclude(const char *directory, bool who)
1566cd6a6acSopenharmony_ci{
1576cd6a6acSopenharmony_ci	struct edir *tmp_list, *current;
1586cd6a6acSopenharmony_ci	size_t len = 0;
1596cd6a6acSopenharmony_ci	int i;
1606cd6a6acSopenharmony_ci
1616cd6a6acSopenharmony_ci	/* Check if already present. */
1626cd6a6acSopenharmony_ci	for (i = 0; i < exclude_count; i++) {
1636cd6a6acSopenharmony_ci		if (strcmp(directory, exclude_lst[i].directory) == 0)
1646cd6a6acSopenharmony_ci			return 0;
1656cd6a6acSopenharmony_ci	}
1666cd6a6acSopenharmony_ci
1676cd6a6acSopenharmony_ci	if (directory == NULL || directory[0] != '/') {
1686cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
1696cd6a6acSopenharmony_ci			    "Full path required for exclude: %s.\n",
1706cd6a6acSopenharmony_ci			    directory);
1716cd6a6acSopenharmony_ci		errno = EINVAL;
1726cd6a6acSopenharmony_ci		return -1;
1736cd6a6acSopenharmony_ci	}
1746cd6a6acSopenharmony_ci
1756cd6a6acSopenharmony_ci	if (exclude_count >= INT_MAX - 1) {
1766cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR, "Too many directory excludes: %d.\n", exclude_count);
1776cd6a6acSopenharmony_ci		errno = EOVERFLOW;
1786cd6a6acSopenharmony_ci		return -1;
1796cd6a6acSopenharmony_ci	}
1806cd6a6acSopenharmony_ci
1816cd6a6acSopenharmony_ci	tmp_list = realloc(exclude_lst,
1826cd6a6acSopenharmony_ci			   sizeof(struct edir) * (exclude_count + 1));
1836cd6a6acSopenharmony_ci	if (!tmp_list)
1846cd6a6acSopenharmony_ci		goto oom;
1856cd6a6acSopenharmony_ci
1866cd6a6acSopenharmony_ci	exclude_lst = tmp_list;
1876cd6a6acSopenharmony_ci
1886cd6a6acSopenharmony_ci	len = strlen(directory);
1896cd6a6acSopenharmony_ci	while (len > 1 && directory[len - 1] == '/')
1906cd6a6acSopenharmony_ci		len--;
1916cd6a6acSopenharmony_ci
1926cd6a6acSopenharmony_ci	current = (exclude_lst + exclude_count);
1936cd6a6acSopenharmony_ci
1946cd6a6acSopenharmony_ci	current->directory = strndup(directory, len);
1956cd6a6acSopenharmony_ci	if (!current->directory)
1966cd6a6acSopenharmony_ci		goto oom;
1976cd6a6acSopenharmony_ci
1986cd6a6acSopenharmony_ci	current->size = len;
1996cd6a6acSopenharmony_ci	current->caller_excluded = who;
2006cd6a6acSopenharmony_ci	exclude_count++;
2016cd6a6acSopenharmony_ci	return 0;
2026cd6a6acSopenharmony_ci
2036cd6a6acSopenharmony_cioom:
2046cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
2056cd6a6acSopenharmony_ci	return -1;
2066cd6a6acSopenharmony_ci}
2076cd6a6acSopenharmony_ci
2086cd6a6acSopenharmony_cistatic int check_excluded(const char *file)
2096cd6a6acSopenharmony_ci{
2106cd6a6acSopenharmony_ci	int i;
2116cd6a6acSopenharmony_ci
2126cd6a6acSopenharmony_ci	for (i = 0; i < exclude_count; i++) {
2136cd6a6acSopenharmony_ci		if (strncmp(file, exclude_lst[i].directory,
2146cd6a6acSopenharmony_ci		    exclude_lst[i].size) == 0) {
2156cd6a6acSopenharmony_ci			if (file[exclude_lst[i].size] == 0 ||
2166cd6a6acSopenharmony_ci					 file[exclude_lst[i].size] == '/')
2176cd6a6acSopenharmony_ci				return 1;
2186cd6a6acSopenharmony_ci		}
2196cd6a6acSopenharmony_ci	}
2206cd6a6acSopenharmony_ci	return 0;
2216cd6a6acSopenharmony_ci}
2226cd6a6acSopenharmony_ci
2236cd6a6acSopenharmony_cistatic uint64_t file_system_count(const char *name)
2246cd6a6acSopenharmony_ci{
2256cd6a6acSopenharmony_ci	struct statvfs statvfs_buf;
2266cd6a6acSopenharmony_ci	uint64_t nfile = 0;
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci	memset(&statvfs_buf, 0, sizeof(statvfs_buf));
2296cd6a6acSopenharmony_ci	if (!statvfs(name, &statvfs_buf))
2306cd6a6acSopenharmony_ci		nfile = statvfs_buf.f_files - statvfs_buf.f_ffree;
2316cd6a6acSopenharmony_ci
2326cd6a6acSopenharmony_ci	return nfile;
2336cd6a6acSopenharmony_ci}
2346cd6a6acSopenharmony_ci
2356cd6a6acSopenharmony_ci/*
2366cd6a6acSopenharmony_ci * This is called once when selinux_restorecon() is first called.
2376cd6a6acSopenharmony_ci * Searches /proc/mounts for all file systems that do not support extended
2386cd6a6acSopenharmony_ci * attributes and adds them to the exclude directory table.  File systems
2396cd6a6acSopenharmony_ci * that support security labels have the seclabel option, return
2406cd6a6acSopenharmony_ci * approximate total file count.
2416cd6a6acSopenharmony_ci */
2426cd6a6acSopenharmony_cistatic uint64_t exclude_non_seclabel_mounts(void)
2436cd6a6acSopenharmony_ci{
2446cd6a6acSopenharmony_ci	struct utsname uts;
2456cd6a6acSopenharmony_ci	FILE *fp;
2466cd6a6acSopenharmony_ci	size_t len;
2476cd6a6acSopenharmony_ci	int index = 0, found = 0;
2486cd6a6acSopenharmony_ci	uint64_t nfile = 0;
2496cd6a6acSopenharmony_ci	char *mount_info[4];
2506cd6a6acSopenharmony_ci	char *buf = NULL, *item;
2516cd6a6acSopenharmony_ci
2526cd6a6acSopenharmony_ci	/* Check to see if the kernel supports seclabel */
2536cd6a6acSopenharmony_ci	if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0)
2546cd6a6acSopenharmony_ci		return 0;
2556cd6a6acSopenharmony_ci	if (is_selinux_enabled() <= 0)
2566cd6a6acSopenharmony_ci		return 0;
2576cd6a6acSopenharmony_ci
2586cd6a6acSopenharmony_ci	fp = fopen("/proc/mounts", "re");
2596cd6a6acSopenharmony_ci	if (!fp)
2606cd6a6acSopenharmony_ci		return 0;
2616cd6a6acSopenharmony_ci
2626cd6a6acSopenharmony_ci	while (getline(&buf, &len, fp) != -1) {
2636cd6a6acSopenharmony_ci		found = 0;
2646cd6a6acSopenharmony_ci		index = 0;
2656cd6a6acSopenharmony_ci		item = strtok(buf, " ");
2666cd6a6acSopenharmony_ci		while (item != NULL) {
2676cd6a6acSopenharmony_ci			mount_info[index] = item;
2686cd6a6acSopenharmony_ci			index++;
2696cd6a6acSopenharmony_ci			if (index == 4)
2706cd6a6acSopenharmony_ci				break;
2716cd6a6acSopenharmony_ci			item = strtok(NULL, " ");
2726cd6a6acSopenharmony_ci		}
2736cd6a6acSopenharmony_ci		if (index < 4) {
2746cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
2756cd6a6acSopenharmony_ci				    "/proc/mounts record \"%s\" has incorrect format.\n",
2766cd6a6acSopenharmony_ci				    buf);
2776cd6a6acSopenharmony_ci			continue;
2786cd6a6acSopenharmony_ci		}
2796cd6a6acSopenharmony_ci
2806cd6a6acSopenharmony_ci		/* Remove pre-existing entry */
2816cd6a6acSopenharmony_ci		remove_exclude(mount_info[1]);
2826cd6a6acSopenharmony_ci
2836cd6a6acSopenharmony_ci		item = strtok(mount_info[3], ",");
2846cd6a6acSopenharmony_ci		while (item != NULL) {
2856cd6a6acSopenharmony_ci			if (strcmp(item, "seclabel") == 0) {
2866cd6a6acSopenharmony_ci				found = 1;
2876cd6a6acSopenharmony_ci				nfile += file_system_count(mount_info[1]);
2886cd6a6acSopenharmony_ci				break;
2896cd6a6acSopenharmony_ci			}
2906cd6a6acSopenharmony_ci			item = strtok(NULL, ",");
2916cd6a6acSopenharmony_ci		}
2926cd6a6acSopenharmony_ci
2936cd6a6acSopenharmony_ci		/* Exclude mount points without the seclabel option */
2946cd6a6acSopenharmony_ci		if (!found) {
2956cd6a6acSopenharmony_ci			if (add_exclude(mount_info[1], !CALLER_EXCLUDED) &&
2966cd6a6acSopenharmony_ci			    errno == ENOMEM)
2976cd6a6acSopenharmony_ci				assert(0);
2986cd6a6acSopenharmony_ci		}
2996cd6a6acSopenharmony_ci	}
3006cd6a6acSopenharmony_ci
3016cd6a6acSopenharmony_ci	free(buf);
3026cd6a6acSopenharmony_ci	fclose(fp);
3036cd6a6acSopenharmony_ci	/* return estimated #Files + 5% for directories and hard links */
3046cd6a6acSopenharmony_ci	return nfile * 1.05;
3056cd6a6acSopenharmony_ci}
3066cd6a6acSopenharmony_ci
3076cd6a6acSopenharmony_ci/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */
3086cd6a6acSopenharmony_cistatic int add_xattr_entry(const char *directory, bool delete_nonmatch,
3096cd6a6acSopenharmony_ci			   bool delete_all)
3106cd6a6acSopenharmony_ci{
3116cd6a6acSopenharmony_ci	char *sha1_buf = NULL;
3126cd6a6acSopenharmony_ci	size_t i, digest_len = 0;
3136cd6a6acSopenharmony_ci	int rc;
3146cd6a6acSopenharmony_ci	enum digest_result digest_result;
3156cd6a6acSopenharmony_ci	bool match;
3166cd6a6acSopenharmony_ci	struct dir_xattr *new_entry;
3176cd6a6acSopenharmony_ci	uint8_t *xattr_digest = NULL;
3186cd6a6acSopenharmony_ci	uint8_t *calculated_digest = NULL;
3196cd6a6acSopenharmony_ci
3206cd6a6acSopenharmony_ci	if (!directory) {
3216cd6a6acSopenharmony_ci		errno = EINVAL;
3226cd6a6acSopenharmony_ci		return -1;
3236cd6a6acSopenharmony_ci	}
3246cd6a6acSopenharmony_ci
3256cd6a6acSopenharmony_ci	match = selabel_get_digests_all_partial_matches(fc_sehandle, directory,
3266cd6a6acSopenharmony_ci								&calculated_digest, &xattr_digest,
3276cd6a6acSopenharmony_ci								&digest_len);
3286cd6a6acSopenharmony_ci
3296cd6a6acSopenharmony_ci	if (!xattr_digest || !digest_len) {
3306cd6a6acSopenharmony_ci		free(calculated_digest);
3316cd6a6acSopenharmony_ci		return 1;
3326cd6a6acSopenharmony_ci	}
3336cd6a6acSopenharmony_ci
3346cd6a6acSopenharmony_ci	/* Convert entry to a hex encoded string. */
3356cd6a6acSopenharmony_ci	sha1_buf = malloc(digest_len * 2 + 1);
3366cd6a6acSopenharmony_ci	if (!sha1_buf) {
3376cd6a6acSopenharmony_ci		free(xattr_digest);
3386cd6a6acSopenharmony_ci		free(calculated_digest);
3396cd6a6acSopenharmony_ci		goto oom;
3406cd6a6acSopenharmony_ci	}
3416cd6a6acSopenharmony_ci
3426cd6a6acSopenharmony_ci	for (i = 0; i < digest_len; i++)
3436cd6a6acSopenharmony_ci		sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]);
3446cd6a6acSopenharmony_ci
3456cd6a6acSopenharmony_ci	digest_result = match ? MATCH : NOMATCH;
3466cd6a6acSopenharmony_ci
3476cd6a6acSopenharmony_ci	if ((delete_nonmatch && !match) || delete_all) {
3486cd6a6acSopenharmony_ci		digest_result = match ? DELETED_MATCH : DELETED_NOMATCH;
3496cd6a6acSopenharmony_ci		rc = removexattr(directory, RESTORECON_PARTIAL_MATCH_DIGEST);
3506cd6a6acSopenharmony_ci		if (rc) {
3516cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
3526cd6a6acSopenharmony_ci				  "Error: %m removing xattr \"%s\" from: %s\n",
3536cd6a6acSopenharmony_ci				  RESTORECON_PARTIAL_MATCH_DIGEST, directory);
3546cd6a6acSopenharmony_ci			digest_result = ERROR;
3556cd6a6acSopenharmony_ci		}
3566cd6a6acSopenharmony_ci	}
3576cd6a6acSopenharmony_ci	free(xattr_digest);
3586cd6a6acSopenharmony_ci	free(calculated_digest);
3596cd6a6acSopenharmony_ci
3606cd6a6acSopenharmony_ci	/* Now add entries to link list. */
3616cd6a6acSopenharmony_ci	new_entry = malloc(sizeof(struct dir_xattr));
3626cd6a6acSopenharmony_ci	if (!new_entry) {
3636cd6a6acSopenharmony_ci		free(sha1_buf);
3646cd6a6acSopenharmony_ci		goto oom;
3656cd6a6acSopenharmony_ci	}
3666cd6a6acSopenharmony_ci	new_entry->next = NULL;
3676cd6a6acSopenharmony_ci
3686cd6a6acSopenharmony_ci	new_entry->directory = strdup(directory);
3696cd6a6acSopenharmony_ci	if (!new_entry->directory) {
3706cd6a6acSopenharmony_ci		free(new_entry);
3716cd6a6acSopenharmony_ci		free(sha1_buf);
3726cd6a6acSopenharmony_ci		goto oom;
3736cd6a6acSopenharmony_ci	}
3746cd6a6acSopenharmony_ci
3756cd6a6acSopenharmony_ci	new_entry->digest = strdup(sha1_buf);
3766cd6a6acSopenharmony_ci	if (!new_entry->digest) {
3776cd6a6acSopenharmony_ci		free(new_entry->directory);
3786cd6a6acSopenharmony_ci		free(new_entry);
3796cd6a6acSopenharmony_ci		free(sha1_buf);
3806cd6a6acSopenharmony_ci		goto oom;
3816cd6a6acSopenharmony_ci	}
3826cd6a6acSopenharmony_ci
3836cd6a6acSopenharmony_ci	new_entry->result = digest_result;
3846cd6a6acSopenharmony_ci
3856cd6a6acSopenharmony_ci	if (!dir_xattr_list) {
3866cd6a6acSopenharmony_ci		dir_xattr_list = new_entry;
3876cd6a6acSopenharmony_ci		dir_xattr_last = new_entry;
3886cd6a6acSopenharmony_ci	} else {
3896cd6a6acSopenharmony_ci		dir_xattr_last->next = new_entry;
3906cd6a6acSopenharmony_ci		dir_xattr_last = new_entry;
3916cd6a6acSopenharmony_ci	}
3926cd6a6acSopenharmony_ci
3936cd6a6acSopenharmony_ci	free(sha1_buf);
3946cd6a6acSopenharmony_ci	return 0;
3956cd6a6acSopenharmony_ci
3966cd6a6acSopenharmony_cioom:
3976cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
3986cd6a6acSopenharmony_ci	return -1;
3996cd6a6acSopenharmony_ci}
4006cd6a6acSopenharmony_ci
4016cd6a6acSopenharmony_ci/*
4026cd6a6acSopenharmony_ci * Support filespec services filespec_add(), filespec_eval() and
4036cd6a6acSopenharmony_ci * filespec_destroy().
4046cd6a6acSopenharmony_ci *
4056cd6a6acSopenharmony_ci * selinux_restorecon(3) uses filespec services when the
4066cd6a6acSopenharmony_ci * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between
4076cd6a6acSopenharmony_ci * an inode and a specification.
4086cd6a6acSopenharmony_ci */
4096cd6a6acSopenharmony_ci
4106cd6a6acSopenharmony_ci/*
4116cd6a6acSopenharmony_ci * The hash table of associations, hashed by inode number. Chaining is used
4126cd6a6acSopenharmony_ci * for collisions, with elements ordered by inode number in each bucket.
4136cd6a6acSopenharmony_ci * Each hash bucket has a dummy header.
4146cd6a6acSopenharmony_ci */
4156cd6a6acSopenharmony_ci#define HASH_BITS 16
4166cd6a6acSopenharmony_ci#define HASH_BUCKETS (1 << HASH_BITS)
4176cd6a6acSopenharmony_ci#define HASH_MASK (HASH_BUCKETS-1)
4186cd6a6acSopenharmony_ci
4196cd6a6acSopenharmony_ci/*
4206cd6a6acSopenharmony_ci * An association between an inode and a context.
4216cd6a6acSopenharmony_ci */
4226cd6a6acSopenharmony_citypedef struct file_spec {
4236cd6a6acSopenharmony_ci	ino_t ino;		/* inode number */
4246cd6a6acSopenharmony_ci	char *con;		/* matched context */
4256cd6a6acSopenharmony_ci	char *file;		/* full pathname */
4266cd6a6acSopenharmony_ci	struct file_spec *next;	/* next association in hash bucket chain */
4276cd6a6acSopenharmony_ci} file_spec_t;
4286cd6a6acSopenharmony_ci
4296cd6a6acSopenharmony_cistatic file_spec_t *fl_head;
4306cd6a6acSopenharmony_cistatic pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER;
4316cd6a6acSopenharmony_ci
4326cd6a6acSopenharmony_ci/*
4336cd6a6acSopenharmony_ci * Try to add an association between an inode and a context. If there is a
4346cd6a6acSopenharmony_ci * different context that matched the inode, then use the first context
4356cd6a6acSopenharmony_ci * that matched.
4366cd6a6acSopenharmony_ci */
4376cd6a6acSopenharmony_cistatic int filespec_add(ino_t ino, const char *con, const char *file,
4386cd6a6acSopenharmony_ci			const struct rest_flags *flags)
4396cd6a6acSopenharmony_ci{
4406cd6a6acSopenharmony_ci	file_spec_t *prevfl, *fl;
4416cd6a6acSopenharmony_ci	uint32_t h;
4426cd6a6acSopenharmony_ci	int ret;
4436cd6a6acSopenharmony_ci	struct stat64 sb;
4446cd6a6acSopenharmony_ci
4456cd6a6acSopenharmony_ci	__pthread_mutex_lock(&fl_mutex);
4466cd6a6acSopenharmony_ci
4476cd6a6acSopenharmony_ci	if (!fl_head) {
4486cd6a6acSopenharmony_ci		fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t));
4496cd6a6acSopenharmony_ci		if (!fl_head)
4506cd6a6acSopenharmony_ci			goto oom;
4516cd6a6acSopenharmony_ci	}
4526cd6a6acSopenharmony_ci
4536cd6a6acSopenharmony_ci	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
4546cd6a6acSopenharmony_ci	for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
4556cd6a6acSopenharmony_ci	     prevfl = fl, fl = fl->next) {
4566cd6a6acSopenharmony_ci		if (ino == fl->ino) {
4576cd6a6acSopenharmony_ci			ret = lstat64(fl->file, &sb);
4586cd6a6acSopenharmony_ci			if (ret < 0 || sb.st_ino != ino) {
4596cd6a6acSopenharmony_ci				freecon(fl->con);
4606cd6a6acSopenharmony_ci				free(fl->file);
4616cd6a6acSopenharmony_ci				fl->file = strdup(file);
4626cd6a6acSopenharmony_ci				if (!fl->file)
4636cd6a6acSopenharmony_ci					goto oom;
4646cd6a6acSopenharmony_ci				fl->con = strdup(con);
4656cd6a6acSopenharmony_ci				if (!fl->con)
4666cd6a6acSopenharmony_ci					goto oom;
4676cd6a6acSopenharmony_ci				goto unlock_1;
4686cd6a6acSopenharmony_ci			}
4696cd6a6acSopenharmony_ci
4706cd6a6acSopenharmony_ci			if (strcmp(fl->con, con) == 0)
4716cd6a6acSopenharmony_ci				goto unlock_1;
4726cd6a6acSopenharmony_ci
4736cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
4746cd6a6acSopenharmony_ci				"conflicting specifications for %s and %s, using %s.\n",
4756cd6a6acSopenharmony_ci				file, fl->file, fl->con);
4766cd6a6acSopenharmony_ci			free(fl->file);
4776cd6a6acSopenharmony_ci			fl->file = strdup(file);
4786cd6a6acSopenharmony_ci			if (!fl->file)
4796cd6a6acSopenharmony_ci				goto oom;
4806cd6a6acSopenharmony_ci
4816cd6a6acSopenharmony_ci			__pthread_mutex_unlock(&fl_mutex);
4826cd6a6acSopenharmony_ci
4836cd6a6acSopenharmony_ci			if (flags->conflicterror) {
4846cd6a6acSopenharmony_ci				selinux_log(SELINUX_ERROR,
4856cd6a6acSopenharmony_ci				"treating conflicting specifications as an error.\n");
4866cd6a6acSopenharmony_ci				return -1;
4876cd6a6acSopenharmony_ci			}
4886cd6a6acSopenharmony_ci			return 1;
4896cd6a6acSopenharmony_ci		}
4906cd6a6acSopenharmony_ci
4916cd6a6acSopenharmony_ci		if (ino > fl->ino)
4926cd6a6acSopenharmony_ci			break;
4936cd6a6acSopenharmony_ci	}
4946cd6a6acSopenharmony_ci
4956cd6a6acSopenharmony_ci	fl = malloc(sizeof(file_spec_t));
4966cd6a6acSopenharmony_ci	if (!fl)
4976cd6a6acSopenharmony_ci		goto oom;
4986cd6a6acSopenharmony_ci	fl->ino = ino;
4996cd6a6acSopenharmony_ci	fl->con = strdup(con);
5006cd6a6acSopenharmony_ci	if (!fl->con)
5016cd6a6acSopenharmony_ci		goto oom_freefl;
5026cd6a6acSopenharmony_ci	fl->file = strdup(file);
5036cd6a6acSopenharmony_ci	if (!fl->file)
5046cd6a6acSopenharmony_ci		goto oom_freeflcon;
5056cd6a6acSopenharmony_ci	fl->next = prevfl->next;
5066cd6a6acSopenharmony_ci	prevfl->next = fl;
5076cd6a6acSopenharmony_ci
5086cd6a6acSopenharmony_ci	__pthread_mutex_unlock(&fl_mutex);
5096cd6a6acSopenharmony_ci	return 0;
5106cd6a6acSopenharmony_ci
5116cd6a6acSopenharmony_cioom_freeflcon:
5126cd6a6acSopenharmony_ci	free(fl->con);
5136cd6a6acSopenharmony_cioom_freefl:
5146cd6a6acSopenharmony_ci	free(fl);
5156cd6a6acSopenharmony_cioom:
5166cd6a6acSopenharmony_ci	__pthread_mutex_unlock(&fl_mutex);
5176cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
5186cd6a6acSopenharmony_ci	return -1;
5196cd6a6acSopenharmony_ciunlock_1:
5206cd6a6acSopenharmony_ci	__pthread_mutex_unlock(&fl_mutex);
5216cd6a6acSopenharmony_ci	return 1;
5226cd6a6acSopenharmony_ci}
5236cd6a6acSopenharmony_ci
5246cd6a6acSopenharmony_ci/*
5256cd6a6acSopenharmony_ci * Evaluate the association hash table distribution.
5266cd6a6acSopenharmony_ci */
5276cd6a6acSopenharmony_ci#ifdef DEBUG
5286cd6a6acSopenharmony_cistatic void filespec_eval(void)
5296cd6a6acSopenharmony_ci{
5306cd6a6acSopenharmony_ci	file_spec_t *fl;
5316cd6a6acSopenharmony_ci	uint32_t h;
5326cd6a6acSopenharmony_ci	size_t used, nel, len, longest;
5336cd6a6acSopenharmony_ci
5346cd6a6acSopenharmony_ci	if (!fl_head)
5356cd6a6acSopenharmony_ci		return;
5366cd6a6acSopenharmony_ci
5376cd6a6acSopenharmony_ci	used = 0;
5386cd6a6acSopenharmony_ci	longest = 0;
5396cd6a6acSopenharmony_ci	nel = 0;
5406cd6a6acSopenharmony_ci	for (h = 0; h < HASH_BUCKETS; h++) {
5416cd6a6acSopenharmony_ci		len = 0;
5426cd6a6acSopenharmony_ci		for (fl = fl_head[h].next; fl; fl = fl->next)
5436cd6a6acSopenharmony_ci			len++;
5446cd6a6acSopenharmony_ci		if (len)
5456cd6a6acSopenharmony_ci			used++;
5466cd6a6acSopenharmony_ci		if (len > longest)
5476cd6a6acSopenharmony_ci			longest = len;
5486cd6a6acSopenharmony_ci		nel += len;
5496cd6a6acSopenharmony_ci	}
5506cd6a6acSopenharmony_ci
5516cd6a6acSopenharmony_ci	selinux_log(SELINUX_INFO,
5526cd6a6acSopenharmony_ci		     "filespec hash table stats: %zu elements, %zu/%zu buckets used, longest chain length %zu\n",
5536cd6a6acSopenharmony_ci		     nel, used, HASH_BUCKETS, longest);
5546cd6a6acSopenharmony_ci}
5556cd6a6acSopenharmony_ci#else
5566cd6a6acSopenharmony_cistatic void filespec_eval(void)
5576cd6a6acSopenharmony_ci{
5586cd6a6acSopenharmony_ci}
5596cd6a6acSopenharmony_ci#endif
5606cd6a6acSopenharmony_ci
5616cd6a6acSopenharmony_ci/*
5626cd6a6acSopenharmony_ci * Destroy the association hash table.
5636cd6a6acSopenharmony_ci */
5646cd6a6acSopenharmony_cistatic void filespec_destroy(void)
5656cd6a6acSopenharmony_ci{
5666cd6a6acSopenharmony_ci	file_spec_t *fl, *tmp;
5676cd6a6acSopenharmony_ci	uint32_t h;
5686cd6a6acSopenharmony_ci
5696cd6a6acSopenharmony_ci	if (!fl_head)
5706cd6a6acSopenharmony_ci		return;
5716cd6a6acSopenharmony_ci
5726cd6a6acSopenharmony_ci	for (h = 0; h < HASH_BUCKETS; h++) {
5736cd6a6acSopenharmony_ci		fl = fl_head[h].next;
5746cd6a6acSopenharmony_ci		while (fl) {
5756cd6a6acSopenharmony_ci			tmp = fl;
5766cd6a6acSopenharmony_ci			fl = fl->next;
5776cd6a6acSopenharmony_ci			freecon(tmp->con);
5786cd6a6acSopenharmony_ci			free(tmp->file);
5796cd6a6acSopenharmony_ci			free(tmp);
5806cd6a6acSopenharmony_ci		}
5816cd6a6acSopenharmony_ci		fl_head[h].next = NULL;
5826cd6a6acSopenharmony_ci	}
5836cd6a6acSopenharmony_ci	free(fl_head);
5846cd6a6acSopenharmony_ci	fl_head = NULL;
5856cd6a6acSopenharmony_ci}
5866cd6a6acSopenharmony_ci
5876cd6a6acSopenharmony_ci/*
5886cd6a6acSopenharmony_ci * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if
5896cd6a6acSopenharmony_ci * the type components differ, updating newtypecon if so.
5906cd6a6acSopenharmony_ci */
5916cd6a6acSopenharmony_cistatic int compare_types(const char *curcon, const char *newcon, char **newtypecon)
5926cd6a6acSopenharmony_ci{
5936cd6a6acSopenharmony_ci	int types_differ = 0;
5946cd6a6acSopenharmony_ci	context_t cona;
5956cd6a6acSopenharmony_ci	context_t conb;
5966cd6a6acSopenharmony_ci	int rc = 0;
5976cd6a6acSopenharmony_ci
5986cd6a6acSopenharmony_ci	cona = context_new(curcon);
5996cd6a6acSopenharmony_ci	if (!cona) {
6006cd6a6acSopenharmony_ci		rc = -1;
6016cd6a6acSopenharmony_ci		goto out;
6026cd6a6acSopenharmony_ci	}
6036cd6a6acSopenharmony_ci	conb = context_new(newcon);
6046cd6a6acSopenharmony_ci	if (!conb) {
6056cd6a6acSopenharmony_ci		context_free(cona);
6066cd6a6acSopenharmony_ci		rc = -1;
6076cd6a6acSopenharmony_ci		goto out;
6086cd6a6acSopenharmony_ci	}
6096cd6a6acSopenharmony_ci
6106cd6a6acSopenharmony_ci	types_differ = strcmp(context_type_get(cona), context_type_get(conb));
6116cd6a6acSopenharmony_ci	if (types_differ) {
6126cd6a6acSopenharmony_ci		rc |= context_user_set(conb, context_user_get(cona));
6136cd6a6acSopenharmony_ci		rc |= context_role_set(conb, context_role_get(cona));
6146cd6a6acSopenharmony_ci		rc |= context_range_set(conb, context_range_get(cona));
6156cd6a6acSopenharmony_ci		if (!rc) {
6166cd6a6acSopenharmony_ci			*newtypecon = strdup(context_str(conb));
6176cd6a6acSopenharmony_ci			if (!*newtypecon) {
6186cd6a6acSopenharmony_ci				rc = -1;
6196cd6a6acSopenharmony_ci				goto err;
6206cd6a6acSopenharmony_ci			}
6216cd6a6acSopenharmony_ci		}
6226cd6a6acSopenharmony_ci	}
6236cd6a6acSopenharmony_ci
6246cd6a6acSopenharmony_cierr:
6256cd6a6acSopenharmony_ci	context_free(cona);
6266cd6a6acSopenharmony_ci	context_free(conb);
6276cd6a6acSopenharmony_ciout:
6286cd6a6acSopenharmony_ci	return rc;
6296cd6a6acSopenharmony_ci}
6306cd6a6acSopenharmony_ci
6316cd6a6acSopenharmony_ci#define DATA_APP_EL1 "/data/app/el1/"
6326cd6a6acSopenharmony_ci#define DATA_APP_EL2 "/data/app/el2/"
6336cd6a6acSopenharmony_ci#define DATA_APP_EL3 "/data/app/el3/"
6346cd6a6acSopenharmony_ci#define DATA_APP_EL4 "/data/app/el4/"
6356cd6a6acSopenharmony_ci#define DATA_ACCOUNTS_ACCOUNT_0 "/data/accounts/account_0/"
6366cd6a6acSopenharmony_ci#define HNP_ROOT_PATH "/data/app/el1/bundle/"
6376cd6a6acSopenharmony_ci#define HNP_PUBLIC_DIR "/hnppublic"
6386cd6a6acSopenharmony_ci#define HNP_ROOT_PATH_LEN 21
6396cd6a6acSopenharmony_ci#define HNP_PUBLIC_DIR_LEN 10
6406cd6a6acSopenharmony_ci
6416cd6a6acSopenharmony_ci// Allow the hnp process to refresh the labels of files in the HNP_ROOT_PATH directory
6426cd6a6acSopenharmony_cistatic bool is_hnp_path(const char *path)
6436cd6a6acSopenharmony_ci{
6446cd6a6acSopenharmony_ci	size_t pathLen = strlen(path);
6456cd6a6acSopenharmony_ci	if ((pathLen < HNP_ROOT_PATH_LEN + 1 + HNP_PUBLIC_DIR_LEN + 1) ||
6466cd6a6acSopenharmony_ci		(strstr(path, HNP_PUBLIC_DIR) == NULL)) {
6476cd6a6acSopenharmony_ci		return false;
6486cd6a6acSopenharmony_ci	}
6496cd6a6acSopenharmony_ci
6506cd6a6acSopenharmony_ci	if (strncmp(path, HNP_ROOT_PATH, HNP_ROOT_PATH_LEN) != 0) {
6516cd6a6acSopenharmony_ci		return false;
6526cd6a6acSopenharmony_ci	}
6536cd6a6acSopenharmony_ci	return true;
6546cd6a6acSopenharmony_ci}
6556cd6a6acSopenharmony_ci
6566cd6a6acSopenharmony_cistatic bool check_path_allow_restorecon(const char *pathname)
6576cd6a6acSopenharmony_ci{
6586cd6a6acSopenharmony_ci	if ((!strncmp(pathname, DATA_APP_EL1, sizeof(DATA_APP_EL1) - 1) && (!is_hnp_path(pathname))) ||
6596cd6a6acSopenharmony_ci		!strncmp(pathname, DATA_APP_EL2, sizeof(DATA_APP_EL2) - 1) ||
6606cd6a6acSopenharmony_ci		!strncmp(pathname, DATA_APP_EL3, sizeof(DATA_APP_EL3) - 1) ||
6616cd6a6acSopenharmony_ci		!strncmp(pathname, DATA_APP_EL4, sizeof(DATA_APP_EL4) - 1) ||
6626cd6a6acSopenharmony_ci		!strncmp(pathname, DATA_ACCOUNTS_ACCOUNT_0, sizeof(DATA_ACCOUNTS_ACCOUNT_0) - 1)) {
6636cd6a6acSopenharmony_ci		return false;
6646cd6a6acSopenharmony_ci	}
6656cd6a6acSopenharmony_ci	return true;
6666cd6a6acSopenharmony_ci}
6676cd6a6acSopenharmony_ci
6686cd6a6acSopenharmony_ci
6696cd6a6acSopenharmony_cistatic int restorecon_sb(const char *pathname, const struct stat *sb,
6706cd6a6acSopenharmony_ci			    const struct rest_flags *flags, bool first)
6716cd6a6acSopenharmony_ci{
6726cd6a6acSopenharmony_ci	char *newcon = NULL;
6736cd6a6acSopenharmony_ci	char *curcon = NULL;
6746cd6a6acSopenharmony_ci	char *newtypecon = NULL;
6756cd6a6acSopenharmony_ci	int rc;
6766cd6a6acSopenharmony_ci	const char *lookup_path = pathname;
6776cd6a6acSopenharmony_ci
6786cd6a6acSopenharmony_ci	if (!check_path_allow_restorecon(pathname)) {
6796cd6a6acSopenharmony_ci		goto out;
6806cd6a6acSopenharmony_ci	}
6816cd6a6acSopenharmony_ci
6826cd6a6acSopenharmony_ci	if (rootpath) {
6836cd6a6acSopenharmony_ci		if (strncmp(rootpath, lookup_path, rootpathlen) != 0) {
6846cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
6856cd6a6acSopenharmony_ci				    "%s is not located in alt_rootpath %s\n",
6866cd6a6acSopenharmony_ci				    lookup_path, rootpath);
6876cd6a6acSopenharmony_ci			return -1;
6886cd6a6acSopenharmony_ci		}
6896cd6a6acSopenharmony_ci		lookup_path += rootpathlen;
6906cd6a6acSopenharmony_ci	}
6916cd6a6acSopenharmony_ci
6926cd6a6acSopenharmony_ci	if (rootpath != NULL && lookup_path[0] == '\0')
6936cd6a6acSopenharmony_ci		/* this is actually the root dir of the alt root. */
6946cd6a6acSopenharmony_ci		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
6956cd6a6acSopenharmony_ci						    sb->st_mode & S_IFMT);
6966cd6a6acSopenharmony_ci	else
6976cd6a6acSopenharmony_ci		rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path,
6986cd6a6acSopenharmony_ci						    sb->st_mode & S_IFMT);
6996cd6a6acSopenharmony_ci
7006cd6a6acSopenharmony_ci	if (rc < 0) {
7016cd6a6acSopenharmony_ci		if (errno == ENOENT) {
7026cd6a6acSopenharmony_ci			if (flags->warnonnomatch && first)
7036cd6a6acSopenharmony_ci				selinux_log(SELINUX_INFO,
7046cd6a6acSopenharmony_ci					    "Warning no default label for %s\n",
7056cd6a6acSopenharmony_ci					    lookup_path);
7066cd6a6acSopenharmony_ci
7076cd6a6acSopenharmony_ci			return 0; /* no match, but not an error */
7086cd6a6acSopenharmony_ci		}
7096cd6a6acSopenharmony_ci
7106cd6a6acSopenharmony_ci		return -1;
7116cd6a6acSopenharmony_ci	}
7126cd6a6acSopenharmony_ci
7136cd6a6acSopenharmony_ci	if (flags->progress) {
7146cd6a6acSopenharmony_ci		__pthread_mutex_lock(&progress_mutex);
7156cd6a6acSopenharmony_ci		fc_count++;
7166cd6a6acSopenharmony_ci		if (fc_count % STAR_COUNT == 0) {
7176cd6a6acSopenharmony_ci			if (flags->mass_relabel && efile_count > 0) {
7186cd6a6acSopenharmony_ci				float pc = (fc_count < efile_count) ? (100.0 *
7196cd6a6acSopenharmony_ci					     fc_count / efile_count) : 100;
7206cd6a6acSopenharmony_ci				fprintf(stdout, "\r%-.1f%%", (double)pc);
7216cd6a6acSopenharmony_ci			} else {
7226cd6a6acSopenharmony_ci				fprintf(stdout, "\r%" PRIu64 "k", fc_count / STAR_COUNT);
7236cd6a6acSopenharmony_ci			}
7246cd6a6acSopenharmony_ci			fflush(stdout);
7256cd6a6acSopenharmony_ci		}
7266cd6a6acSopenharmony_ci		__pthread_mutex_unlock(&progress_mutex);
7276cd6a6acSopenharmony_ci	}
7286cd6a6acSopenharmony_ci
7296cd6a6acSopenharmony_ci	if (flags->add_assoc) {
7306cd6a6acSopenharmony_ci		rc = filespec_add(sb->st_ino, newcon, pathname, flags);
7316cd6a6acSopenharmony_ci
7326cd6a6acSopenharmony_ci		if (rc < 0) {
7336cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
7346cd6a6acSopenharmony_ci				    "filespec_add error: %s\n", pathname);
7356cd6a6acSopenharmony_ci			freecon(newcon);
7366cd6a6acSopenharmony_ci			return -1;
7376cd6a6acSopenharmony_ci		}
7386cd6a6acSopenharmony_ci
7396cd6a6acSopenharmony_ci		if (rc > 0) {
7406cd6a6acSopenharmony_ci			/* Already an association and it took precedence. */
7416cd6a6acSopenharmony_ci			freecon(newcon);
7426cd6a6acSopenharmony_ci			return 0;
7436cd6a6acSopenharmony_ci		}
7446cd6a6acSopenharmony_ci	}
7456cd6a6acSopenharmony_ci
7466cd6a6acSopenharmony_ci	if (flags->log_matches)
7476cd6a6acSopenharmony_ci		selinux_log(SELINUX_INFO, "%s matched by %s\n",
7486cd6a6acSopenharmony_ci			    pathname, newcon);
7496cd6a6acSopenharmony_ci
7506cd6a6acSopenharmony_ci	if (lgetfilecon_raw(pathname, &curcon) < 0) {
7516cd6a6acSopenharmony_ci		if (errno != ENODATA)
7526cd6a6acSopenharmony_ci			goto err;
7536cd6a6acSopenharmony_ci
7546cd6a6acSopenharmony_ci		curcon = NULL;
7556cd6a6acSopenharmony_ci	}
7566cd6a6acSopenharmony_ci
7576cd6a6acSopenharmony_ci	if (curcon == NULL || strcmp(curcon, newcon) != 0) {
7586cd6a6acSopenharmony_ci		bool updated = false;
7596cd6a6acSopenharmony_ci
7606cd6a6acSopenharmony_ci		if (!flags->set_specctx && curcon &&
7616cd6a6acSopenharmony_ci				    (is_context_customizable(curcon) > 0)) {
7626cd6a6acSopenharmony_ci			if (flags->verbose) {
7636cd6a6acSopenharmony_ci				selinux_log(SELINUX_INFO,
7646cd6a6acSopenharmony_ci				 "%s not reset as customized by admin to %s\n",
7656cd6a6acSopenharmony_ci							    pathname, curcon);
7666cd6a6acSopenharmony_ci			}
7676cd6a6acSopenharmony_ci			goto out;
7686cd6a6acSopenharmony_ci		}
7696cd6a6acSopenharmony_ci
7706cd6a6acSopenharmony_ci		if (!flags->set_specctx && curcon) {
7716cd6a6acSopenharmony_ci			/* If types different then update newcon. */
7726cd6a6acSopenharmony_ci			rc = compare_types(curcon, newcon, &newtypecon);
7736cd6a6acSopenharmony_ci			if (rc)
7746cd6a6acSopenharmony_ci				goto err;
7756cd6a6acSopenharmony_ci
7766cd6a6acSopenharmony_ci			if (newtypecon) {
7776cd6a6acSopenharmony_ci				freecon(newcon);
7786cd6a6acSopenharmony_ci				newcon = newtypecon;
7796cd6a6acSopenharmony_ci			} else {
7806cd6a6acSopenharmony_ci				goto out;
7816cd6a6acSopenharmony_ci			}
7826cd6a6acSopenharmony_ci		}
7836cd6a6acSopenharmony_ci
7846cd6a6acSopenharmony_ci		if (!flags->nochange) {
7856cd6a6acSopenharmony_ci			if (lsetfilecon(pathname, newcon) < 0)
7866cd6a6acSopenharmony_ci				goto err;
7876cd6a6acSopenharmony_ci			updated = true;
7886cd6a6acSopenharmony_ci		}
7896cd6a6acSopenharmony_ci
7906cd6a6acSopenharmony_ci		if (flags->verbose)
7916cd6a6acSopenharmony_ci			selinux_log(SELINUX_INFO,
7926cd6a6acSopenharmony_ci				    "%s %s from %s to %s\n",
7936cd6a6acSopenharmony_ci				    updated ? "Relabeled" : "Would relabel",
7946cd6a6acSopenharmony_ci				    pathname,
7956cd6a6acSopenharmony_ci				    curcon ? curcon : "<no context>",
7966cd6a6acSopenharmony_ci				    newcon);
7976cd6a6acSopenharmony_ci
7986cd6a6acSopenharmony_ci		if (flags->syslog_changes && !flags->nochange) {
7996cd6a6acSopenharmony_ci			if (curcon)
8006cd6a6acSopenharmony_ci				syslog(LOG_INFO,
8016cd6a6acSopenharmony_ci					    "relabeling %s from %s to %s\n",
8026cd6a6acSopenharmony_ci					    pathname, curcon, newcon);
8036cd6a6acSopenharmony_ci			else
8046cd6a6acSopenharmony_ci				syslog(LOG_INFO, "labeling %s to %s\n",
8056cd6a6acSopenharmony_ci					    pathname, newcon);
8066cd6a6acSopenharmony_ci		}
8076cd6a6acSopenharmony_ci	}
8086cd6a6acSopenharmony_ci
8096cd6a6acSopenharmony_ciout:
8106cd6a6acSopenharmony_ci	rc = 0;
8116cd6a6acSopenharmony_ciout1:
8126cd6a6acSopenharmony_ci	freecon(curcon);
8136cd6a6acSopenharmony_ci	freecon(newcon);
8146cd6a6acSopenharmony_ci	return rc;
8156cd6a6acSopenharmony_cierr:
8166cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR,
8176cd6a6acSopenharmony_ci		    "Could not set context for %s:  %m\n",
8186cd6a6acSopenharmony_ci		    pathname);
8196cd6a6acSopenharmony_ci	rc = -1;
8206cd6a6acSopenharmony_ci	goto out1;
8216cd6a6acSopenharmony_ci}
8226cd6a6acSopenharmony_ci
8236cd6a6acSopenharmony_cistruct dir_hash_node {
8246cd6a6acSopenharmony_ci	char *path;
8256cd6a6acSopenharmony_ci	uint8_t digest[SHA1_HASH_SIZE];
8266cd6a6acSopenharmony_ci	struct dir_hash_node *next;
8276cd6a6acSopenharmony_ci};
8286cd6a6acSopenharmony_ci/*
8296cd6a6acSopenharmony_ci * Returns true if the digest of all partial matched contexts is the same as
8306cd6a6acSopenharmony_ci * the one saved by setxattr. Otherwise returns false and constructs a
8316cd6a6acSopenharmony_ci * dir_hash_node with the newly calculated digest.
8326cd6a6acSopenharmony_ci */
8336cd6a6acSopenharmony_cistatic bool check_context_match_for_dir(const char *pathname,
8346cd6a6acSopenharmony_ci					struct dir_hash_node **new_node,
8356cd6a6acSopenharmony_ci					int error)
8366cd6a6acSopenharmony_ci{
8376cd6a6acSopenharmony_ci	bool status;
8386cd6a6acSopenharmony_ci	size_t digest_len = 0;
8396cd6a6acSopenharmony_ci	uint8_t *read_digest = NULL;
8406cd6a6acSopenharmony_ci	uint8_t *calculated_digest = NULL;
8416cd6a6acSopenharmony_ci
8426cd6a6acSopenharmony_ci	if (!new_node)
8436cd6a6acSopenharmony_ci		return false;
8446cd6a6acSopenharmony_ci
8456cd6a6acSopenharmony_ci	*new_node = NULL;
8466cd6a6acSopenharmony_ci
8476cd6a6acSopenharmony_ci	/* status = true if digests match, false otherwise. */
8486cd6a6acSopenharmony_ci	status = selabel_get_digests_all_partial_matches(fc_sehandle, pathname,
8496cd6a6acSopenharmony_ci							 &calculated_digest,
8506cd6a6acSopenharmony_ci							 &read_digest,
8516cd6a6acSopenharmony_ci							 &digest_len);
8526cd6a6acSopenharmony_ci
8536cd6a6acSopenharmony_ci	if (status)
8546cd6a6acSopenharmony_ci		goto free;
8556cd6a6acSopenharmony_ci
8566cd6a6acSopenharmony_ci	/* Save digest of all matched contexts for the current directory. */
8576cd6a6acSopenharmony_ci	if (!error && calculated_digest) {
8586cd6a6acSopenharmony_ci		*new_node = calloc(1, sizeof(struct dir_hash_node));
8596cd6a6acSopenharmony_ci
8606cd6a6acSopenharmony_ci		if (!*new_node)
8616cd6a6acSopenharmony_ci			goto oom;
8626cd6a6acSopenharmony_ci
8636cd6a6acSopenharmony_ci		(*new_node)->path = strdup(pathname);
8646cd6a6acSopenharmony_ci
8656cd6a6acSopenharmony_ci		if (!(*new_node)->path) {
8666cd6a6acSopenharmony_ci			free(*new_node);
8676cd6a6acSopenharmony_ci			*new_node = NULL;
8686cd6a6acSopenharmony_ci			goto oom;
8696cd6a6acSopenharmony_ci		}
8706cd6a6acSopenharmony_ci		memcpy((*new_node)->digest, calculated_digest, digest_len);
8716cd6a6acSopenharmony_ci		(*new_node)->next = NULL;
8726cd6a6acSopenharmony_ci	}
8736cd6a6acSopenharmony_ci
8746cd6a6acSopenharmony_cifree:
8756cd6a6acSopenharmony_ci	free(calculated_digest);
8766cd6a6acSopenharmony_ci	free(read_digest);
8776cd6a6acSopenharmony_ci	return status;
8786cd6a6acSopenharmony_ci
8796cd6a6acSopenharmony_cioom:
8806cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
8816cd6a6acSopenharmony_ci	goto free;
8826cd6a6acSopenharmony_ci}
8836cd6a6acSopenharmony_ci
8846cd6a6acSopenharmony_cistatic bool is_in_skip_elx(const char *path) {
8856cd6a6acSopenharmony_ci	if (sizeof(skip_elx_path) == 0) {
8866cd6a6acSopenharmony_ci		return false;
8876cd6a6acSopenharmony_ci	}
8886cd6a6acSopenharmony_ci	size_t len = sizeof(skip_elx_path) / sizeof(skip_elx_path[0]);
8896cd6a6acSopenharmony_ci	for (size_t i = 0; i < len; i++) {
8906cd6a6acSopenharmony_ci		if (strncmp(path, skip_elx_path[i], strlen(skip_elx_path[i])) == 0) {
8916cd6a6acSopenharmony_ci			return true;
8926cd6a6acSopenharmony_ci		}
8936cd6a6acSopenharmony_ci	}
8946cd6a6acSopenharmony_ci	return false;
8956cd6a6acSopenharmony_ci}
8966cd6a6acSopenharmony_ci
8976cd6a6acSopenharmony_cistruct rest_state {
8986cd6a6acSopenharmony_ci	struct rest_flags flags;
8996cd6a6acSopenharmony_ci	dev_t dev_num;
9006cd6a6acSopenharmony_ci	struct statfs sfsb;
9016cd6a6acSopenharmony_ci	bool ignore_digest;
9026cd6a6acSopenharmony_ci	bool setrestorecondigest;
9036cd6a6acSopenharmony_ci	bool parallel;
9046cd6a6acSopenharmony_ci
9056cd6a6acSopenharmony_ci	FTS *fts;
9066cd6a6acSopenharmony_ci	FTSENT *ftsent_first;
9076cd6a6acSopenharmony_ci	struct dir_hash_node *head, *current;
9086cd6a6acSopenharmony_ci	bool abort;
9096cd6a6acSopenharmony_ci	int error;
9106cd6a6acSopenharmony_ci	long unsigned skipped_errors;
9116cd6a6acSopenharmony_ci	int saved_errno;
9126cd6a6acSopenharmony_ci	pthread_mutex_t mutex;
9136cd6a6acSopenharmony_ci};
9146cd6a6acSopenharmony_ci
9156cd6a6acSopenharmony_cistatic void *selinux_restorecon_thread(void *arg)
9166cd6a6acSopenharmony_ci{
9176cd6a6acSopenharmony_ci	struct rest_state *state = arg;
9186cd6a6acSopenharmony_ci	FTS *fts = state->fts;
9196cd6a6acSopenharmony_ci	FTSENT *ftsent;
9206cd6a6acSopenharmony_ci	int error;
9216cd6a6acSopenharmony_ci	char ent_path[PATH_MAX];
9226cd6a6acSopenharmony_ci	struct stat ent_st;
9236cd6a6acSopenharmony_ci	bool first = false;
9246cd6a6acSopenharmony_ci
9256cd6a6acSopenharmony_ci	if (state->parallel)
9266cd6a6acSopenharmony_ci		pthread_mutex_lock(&state->mutex);
9276cd6a6acSopenharmony_ci
9286cd6a6acSopenharmony_ci	if (state->ftsent_first) {
9296cd6a6acSopenharmony_ci		ftsent = state->ftsent_first;
9306cd6a6acSopenharmony_ci		state->ftsent_first = NULL;
9316cd6a6acSopenharmony_ci		first = true;
9326cd6a6acSopenharmony_ci		goto loop_body;
9336cd6a6acSopenharmony_ci	}
9346cd6a6acSopenharmony_ci
9356cd6a6acSopenharmony_ci	while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) {
9366cd6a6acSopenharmony_ciloop_body:
9376cd6a6acSopenharmony_ci		/* If the FTS_XDEV flag is set and the device is different */
9386cd6a6acSopenharmony_ci		if (state->flags.set_xdev &&
9396cd6a6acSopenharmony_ci		    ftsent->fts_statp->st_dev != state->dev_num)
9406cd6a6acSopenharmony_ci			continue;
9416cd6a6acSopenharmony_ci
9426cd6a6acSopenharmony_ci		switch (ftsent->fts_info) {
9436cd6a6acSopenharmony_ci		case FTS_DC:
9446cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
9456cd6a6acSopenharmony_ci				    "Directory cycle on %s.\n",
9466cd6a6acSopenharmony_ci				    ftsent->fts_path);
9476cd6a6acSopenharmony_ci			errno = ELOOP;
9486cd6a6acSopenharmony_ci			state->error = -1;
9496cd6a6acSopenharmony_ci			state->abort = true;
9506cd6a6acSopenharmony_ci			goto finish;
9516cd6a6acSopenharmony_ci		case FTS_DP:
9526cd6a6acSopenharmony_ci			continue;
9536cd6a6acSopenharmony_ci		case FTS_DNR:
9546cd6a6acSopenharmony_ci			error = errno;
9556cd6a6acSopenharmony_ci			errno = ftsent->fts_errno;
9566cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
9576cd6a6acSopenharmony_ci				    "Could not read %s: %m.\n",
9586cd6a6acSopenharmony_ci				    ftsent->fts_path);
9596cd6a6acSopenharmony_ci			errno = error;
9606cd6a6acSopenharmony_ci			fts_set(fts, ftsent, FTS_SKIP);
9616cd6a6acSopenharmony_ci			continue;
9626cd6a6acSopenharmony_ci		case FTS_NS:
9636cd6a6acSopenharmony_ci			error = errno;
9646cd6a6acSopenharmony_ci			errno = ftsent->fts_errno;
9656cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
9666cd6a6acSopenharmony_ci				    "Could not stat %s: %m.\n",
9676cd6a6acSopenharmony_ci				    ftsent->fts_path);
9686cd6a6acSopenharmony_ci			errno = error;
9696cd6a6acSopenharmony_ci			fts_set(fts, ftsent, FTS_SKIP);
9706cd6a6acSopenharmony_ci			continue;
9716cd6a6acSopenharmony_ci		case FTS_ERR:
9726cd6a6acSopenharmony_ci			error = errno;
9736cd6a6acSopenharmony_ci			errno = ftsent->fts_errno;
9746cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
9756cd6a6acSopenharmony_ci				    "Error on %s: %m.\n",
9766cd6a6acSopenharmony_ci				    ftsent->fts_path);
9776cd6a6acSopenharmony_ci			errno = error;
9786cd6a6acSopenharmony_ci			fts_set(fts, ftsent, FTS_SKIP);
9796cd6a6acSopenharmony_ci			continue;
9806cd6a6acSopenharmony_ci		case FTS_D:
9816cd6a6acSopenharmony_ci			if (state->sfsb.f_type == SYSFS_MAGIC &&
9826cd6a6acSopenharmony_ci			    !selabel_partial_match(fc_sehandle,
9836cd6a6acSopenharmony_ci			    ftsent->fts_path)) {
9846cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
9856cd6a6acSopenharmony_ci				continue;
9866cd6a6acSopenharmony_ci			}
9876cd6a6acSopenharmony_ci
9886cd6a6acSopenharmony_ci			if (check_excluded(ftsent->fts_path)) {
9896cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
9906cd6a6acSopenharmony_ci				continue;
9916cd6a6acSopenharmony_ci			}
9926cd6a6acSopenharmony_ci
9936cd6a6acSopenharmony_ci			if (state->setrestorecondigest) {
9946cd6a6acSopenharmony_ci				struct dir_hash_node *new_node = NULL;
9956cd6a6acSopenharmony_ci
9966cd6a6acSopenharmony_ci				if (check_context_match_for_dir(ftsent->fts_path,
9976cd6a6acSopenharmony_ci								&new_node,
9986cd6a6acSopenharmony_ci								state->error) &&
9996cd6a6acSopenharmony_ci								!state->ignore_digest) {
10006cd6a6acSopenharmony_ci					selinux_log(SELINUX_INFO,
10016cd6a6acSopenharmony_ci						"Skipping restorecon on directory(%s)\n",
10026cd6a6acSopenharmony_ci						    ftsent->fts_path);
10036cd6a6acSopenharmony_ci					fts_set(fts, ftsent, FTS_SKIP);
10046cd6a6acSopenharmony_ci					continue;
10056cd6a6acSopenharmony_ci				}
10066cd6a6acSopenharmony_ci
10076cd6a6acSopenharmony_ci				if (new_node && !state->error) {
10086cd6a6acSopenharmony_ci					if (!state->current) {
10096cd6a6acSopenharmony_ci						state->current = new_node;
10106cd6a6acSopenharmony_ci						state->head = state->current;
10116cd6a6acSopenharmony_ci					} else {
10126cd6a6acSopenharmony_ci						state->current->next = new_node;
10136cd6a6acSopenharmony_ci						state->current = new_node;
10146cd6a6acSopenharmony_ci					}
10156cd6a6acSopenharmony_ci				}
10166cd6a6acSopenharmony_ci			}
10176cd6a6acSopenharmony_ci
10186cd6a6acSopenharmony_ci			if (state->flags.skipelx && is_in_skip_elx(ftsent->fts_path)) {
10196cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
10206cd6a6acSopenharmony_ci				continue;
10216cd6a6acSopenharmony_ci			}
10226cd6a6acSopenharmony_ci
10236cd6a6acSopenharmony_ci			enum skip_type skip_ignore_flag = skip_ignore_relabel(ftsent->fts_path);
10246cd6a6acSopenharmony_ci			selinux_log(SELINUX_INFO,
10256cd6a6acSopenharmony_ci						"ignore cfg parsing result %d \n",
10266cd6a6acSopenharmony_ci						skip_ignore_flag);
10276cd6a6acSopenharmony_ci			switch (skip_ignore_flag) {
10286cd6a6acSopenharmony_ci			case SKIP_SELF_SUB_DIR:
10296cd6a6acSopenharmony_ci				selinux_log(SELINUX_INFO,
10306cd6a6acSopenharmony_ci							"Skipping restorecon on directory(%s), cause ignroe_cfg\n",
10316cd6a6acSopenharmony_ci							ftsent->fts_path);
10326cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
10336cd6a6acSopenharmony_ci				continue;
10346cd6a6acSopenharmony_ci			case SKIP_SUB_DIR:
10356cd6a6acSopenharmony_ci				selinux_log(SELINUX_INFO,
10366cd6a6acSopenharmony_ci							"Skipping restorecon on directory(%s) sub directory, cause ignroe_cfg\n",
10376cd6a6acSopenharmony_ci							ftsent->fts_path);
10386cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
10396cd6a6acSopenharmony_ci			default:
10406cd6a6acSopenharmony_ci				break;
10416cd6a6acSopenharmony_ci			}
10426cd6a6acSopenharmony_ci
10436cd6a6acSopenharmony_ci			/* fall through */
10446cd6a6acSopenharmony_ci		default:
10456cd6a6acSopenharmony_ci			if (strlcpy(ent_path, ftsent->fts_path, sizeof(ent_path)) >= sizeof(ent_path)) {
10466cd6a6acSopenharmony_ci				selinux_log(SELINUX_ERROR,
10476cd6a6acSopenharmony_ci					    "Path name too long on %s.\n",
10486cd6a6acSopenharmony_ci					    ftsent->fts_path);
10496cd6a6acSopenharmony_ci				errno = ENAMETOOLONG;
10506cd6a6acSopenharmony_ci				state->error = -1;
10516cd6a6acSopenharmony_ci				state->abort = true;
10526cd6a6acSopenharmony_ci				goto finish;
10536cd6a6acSopenharmony_ci			}
10546cd6a6acSopenharmony_ci
10556cd6a6acSopenharmony_ci			ent_st = *ftsent->fts_statp;
10566cd6a6acSopenharmony_ci			if (state->parallel)
10576cd6a6acSopenharmony_ci				pthread_mutex_unlock(&state->mutex);
10586cd6a6acSopenharmony_ci
10596cd6a6acSopenharmony_ci			error = restorecon_sb(ent_path, &ent_st, &state->flags,
10606cd6a6acSopenharmony_ci					      first);
10616cd6a6acSopenharmony_ci
10626cd6a6acSopenharmony_ci			if (state->parallel) {
10636cd6a6acSopenharmony_ci				pthread_mutex_lock(&state->mutex);
10646cd6a6acSopenharmony_ci				if (state->abort)
10656cd6a6acSopenharmony_ci					goto unlock;
10666cd6a6acSopenharmony_ci			}
10676cd6a6acSopenharmony_ci
10686cd6a6acSopenharmony_ci			first = false;
10696cd6a6acSopenharmony_ci			if (error) {
10706cd6a6acSopenharmony_ci				if (state->flags.abort_on_error) {
10716cd6a6acSopenharmony_ci					state->error = error;
10726cd6a6acSopenharmony_ci					state->abort = true;
10736cd6a6acSopenharmony_ci					goto finish;
10746cd6a6acSopenharmony_ci				}
10756cd6a6acSopenharmony_ci				if (state->flags.count_errors)
10766cd6a6acSopenharmony_ci					state->skipped_errors++;
10776cd6a6acSopenharmony_ci				else
10786cd6a6acSopenharmony_ci					state->error = error;
10796cd6a6acSopenharmony_ci			}
10806cd6a6acSopenharmony_ci			break;
10816cd6a6acSopenharmony_ci		}
10826cd6a6acSopenharmony_ci	}
10836cd6a6acSopenharmony_ci
10846cd6a6acSopenharmony_cifinish:
10856cd6a6acSopenharmony_ci	if (!state->saved_errno)
10866cd6a6acSopenharmony_ci		state->saved_errno = errno;
10876cd6a6acSopenharmony_ciunlock:
10886cd6a6acSopenharmony_ci	if (state->parallel)
10896cd6a6acSopenharmony_ci		pthread_mutex_unlock(&state->mutex);
10906cd6a6acSopenharmony_ci	return NULL;
10916cd6a6acSopenharmony_ci}
10926cd6a6acSopenharmony_ci
10936cd6a6acSopenharmony_cistatic int selinux_restorecon_common(const char *pathname_orig,
10946cd6a6acSopenharmony_ci				     unsigned int restorecon_flags,
10956cd6a6acSopenharmony_ci				     size_t nthreads)
10966cd6a6acSopenharmony_ci{
10976cd6a6acSopenharmony_ci	struct rest_state state;
10986cd6a6acSopenharmony_ci
10996cd6a6acSopenharmony_ci	state.flags.nochange = (restorecon_flags &
11006cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
11016cd6a6acSopenharmony_ci	state.flags.verbose = (restorecon_flags &
11026cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_VERBOSE) ? true : false;
11036cd6a6acSopenharmony_ci	state.flags.progress = (restorecon_flags &
11046cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_PROGRESS) ? true : false;
11056cd6a6acSopenharmony_ci	state.flags.mass_relabel = (restorecon_flags &
11066cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
11076cd6a6acSopenharmony_ci	state.flags.recurse = (restorecon_flags &
11086cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_RECURSE) ? true : false;
11096cd6a6acSopenharmony_ci	state.flags.set_specctx = (restorecon_flags &
11106cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
11116cd6a6acSopenharmony_ci	state.flags.userealpath = (restorecon_flags &
11126cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_REALPATH) ? true : false;
11136cd6a6acSopenharmony_ci	state.flags.set_xdev = (restorecon_flags &
11146cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_XDEV) ? true : false;
11156cd6a6acSopenharmony_ci	state.flags.add_assoc = (restorecon_flags &
11166cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
11176cd6a6acSopenharmony_ci	state.flags.abort_on_error = (restorecon_flags &
11186cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
11196cd6a6acSopenharmony_ci	state.flags.syslog_changes = (restorecon_flags &
11206cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
11216cd6a6acSopenharmony_ci	state.flags.log_matches = (restorecon_flags &
11226cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
11236cd6a6acSopenharmony_ci	state.flags.ignore_noent = (restorecon_flags &
11246cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
11256cd6a6acSopenharmony_ci	state.flags.warnonnomatch = true;
11266cd6a6acSopenharmony_ci	state.flags.conflicterror = (restorecon_flags &
11276cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false;
11286cd6a6acSopenharmony_ci	ignore_mounts = (restorecon_flags &
11296cd6a6acSopenharmony_ci		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
11306cd6a6acSopenharmony_ci	state.ignore_digest = (restorecon_flags &
11316cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
11326cd6a6acSopenharmony_ci	state.flags.count_errors = (restorecon_flags &
11336cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_COUNT_ERRORS) ? true : false;
11346cd6a6acSopenharmony_ci	state.flags.skipelx = (restorecon_flags &
11356cd6a6acSopenharmony_ci		    SELINUX_RESTORECON_SKIPELX) ? true : false;
11366cd6a6acSopenharmony_ci	state.setrestorecondigest = true;
11376cd6a6acSopenharmony_ci
11386cd6a6acSopenharmony_ci	state.head = NULL;
11396cd6a6acSopenharmony_ci	state.current = NULL;
11406cd6a6acSopenharmony_ci	state.abort = false;
11416cd6a6acSopenharmony_ci	state.error = 0;
11426cd6a6acSopenharmony_ci	state.skipped_errors = 0;
11436cd6a6acSopenharmony_ci	state.saved_errno = 0;
11446cd6a6acSopenharmony_ci
11456cd6a6acSopenharmony_ci	struct stat sb;
11466cd6a6acSopenharmony_ci	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
11476cd6a6acSopenharmony_ci	char *paths[2] = { NULL, NULL };
11486cd6a6acSopenharmony_ci	int fts_flags, error;
11496cd6a6acSopenharmony_ci	struct dir_hash_node *current = NULL;
11506cd6a6acSopenharmony_ci
11516cd6a6acSopenharmony_ci	if (state.flags.verbose && state.flags.progress)
11526cd6a6acSopenharmony_ci		state.flags.verbose = false;
11536cd6a6acSopenharmony_ci
11546cd6a6acSopenharmony_ci	__selinux_once(fc_once, restorecon_init);
11556cd6a6acSopenharmony_ci
11566cd6a6acSopenharmony_ci	if (!fc_sehandle)
11576cd6a6acSopenharmony_ci		return -1;
11586cd6a6acSopenharmony_ci
11596cd6a6acSopenharmony_ci	/*
11606cd6a6acSopenharmony_ci	 * If selabel_no_digest = true then no digest has been requested by
11616cd6a6acSopenharmony_ci	 * an external selabel_open(3) call.
11626cd6a6acSopenharmony_ci	 */
11636cd6a6acSopenharmony_ci	if (selabel_no_digest ||
11646cd6a6acSopenharmony_ci	    (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST))
11656cd6a6acSopenharmony_ci		state.setrestorecondigest = false;
11666cd6a6acSopenharmony_ci
11676cd6a6acSopenharmony_ci	if (!__pthread_supported) {
11686cd6a6acSopenharmony_ci		if (nthreads != 1) {
11696cd6a6acSopenharmony_ci			nthreads = 1;
11706cd6a6acSopenharmony_ci			selinux_log(SELINUX_WARNING,
11716cd6a6acSopenharmony_ci				"Threading functionality not available, falling back to 1 thread.");
11726cd6a6acSopenharmony_ci		}
11736cd6a6acSopenharmony_ci	} else if (nthreads == 0) {
11746cd6a6acSopenharmony_ci		long nproc = sysconf(_SC_NPROCESSORS_ONLN);
11756cd6a6acSopenharmony_ci
11766cd6a6acSopenharmony_ci		if (nproc > 0) {
11776cd6a6acSopenharmony_ci			nthreads = nproc;
11786cd6a6acSopenharmony_ci		} else {
11796cd6a6acSopenharmony_ci			nthreads = 1;
11806cd6a6acSopenharmony_ci			selinux_log(SELINUX_WARNING,
11816cd6a6acSopenharmony_ci				"Unable to detect CPU count, falling back to 1 thread.");
11826cd6a6acSopenharmony_ci		}
11836cd6a6acSopenharmony_ci	}
11846cd6a6acSopenharmony_ci
11856cd6a6acSopenharmony_ci	/*
11866cd6a6acSopenharmony_ci	 * Convert passed-in pathname to canonical pathname by resolving
11876cd6a6acSopenharmony_ci	 * realpath of containing dir, then appending last component name.
11886cd6a6acSopenharmony_ci	 */
11896cd6a6acSopenharmony_ci	if (state.flags.userealpath) {
11906cd6a6acSopenharmony_ci		char *basename_cpy = strdup(pathname_orig);
11916cd6a6acSopenharmony_ci		if (!basename_cpy)
11926cd6a6acSopenharmony_ci			goto realpatherr;
11936cd6a6acSopenharmony_ci		pathbname = basename(basename_cpy);
11946cd6a6acSopenharmony_ci		if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") ||
11956cd6a6acSopenharmony_ci					    !strcmp(pathbname, "..")) {
11966cd6a6acSopenharmony_ci			pathname = realpath(pathname_orig, NULL);
11976cd6a6acSopenharmony_ci			if (!pathname) {
11986cd6a6acSopenharmony_ci				free(basename_cpy);
11996cd6a6acSopenharmony_ci				/* missing parent directory */
12006cd6a6acSopenharmony_ci				if (state.flags.ignore_noent && errno == ENOENT) {
12016cd6a6acSopenharmony_ci					return 0;
12026cd6a6acSopenharmony_ci				}
12036cd6a6acSopenharmony_ci				goto realpatherr;
12046cd6a6acSopenharmony_ci			}
12056cd6a6acSopenharmony_ci		} else {
12066cd6a6acSopenharmony_ci			char *dirname_cpy = strdup(pathname_orig);
12076cd6a6acSopenharmony_ci			if (!dirname_cpy) {
12086cd6a6acSopenharmony_ci				free(basename_cpy);
12096cd6a6acSopenharmony_ci				goto realpatherr;
12106cd6a6acSopenharmony_ci			}
12116cd6a6acSopenharmony_ci			pathdname = dirname(dirname_cpy);
12126cd6a6acSopenharmony_ci			pathdnamer = realpath(pathdname, NULL);
12136cd6a6acSopenharmony_ci			free(dirname_cpy);
12146cd6a6acSopenharmony_ci			if (!pathdnamer) {
12156cd6a6acSopenharmony_ci				free(basename_cpy);
12166cd6a6acSopenharmony_ci				if (state.flags.ignore_noent && errno == ENOENT) {
12176cd6a6acSopenharmony_ci					return 0;
12186cd6a6acSopenharmony_ci				}
12196cd6a6acSopenharmony_ci				goto realpatherr;
12206cd6a6acSopenharmony_ci			}
12216cd6a6acSopenharmony_ci			if (!strcmp(pathdnamer, "/"))
12226cd6a6acSopenharmony_ci				error = asprintf(&pathname, "/%s", pathbname);
12236cd6a6acSopenharmony_ci			else
12246cd6a6acSopenharmony_ci				error = asprintf(&pathname, "%s/%s",
12256cd6a6acSopenharmony_ci						    pathdnamer, pathbname);
12266cd6a6acSopenharmony_ci			if (error < 0) {
12276cd6a6acSopenharmony_ci				free(basename_cpy);
12286cd6a6acSopenharmony_ci				goto oom;
12296cd6a6acSopenharmony_ci			}
12306cd6a6acSopenharmony_ci		}
12316cd6a6acSopenharmony_ci		free(basename_cpy);
12326cd6a6acSopenharmony_ci	} else {
12336cd6a6acSopenharmony_ci		pathname = strdup(pathname_orig);
12346cd6a6acSopenharmony_ci		if (!pathname)
12356cd6a6acSopenharmony_ci			goto oom;
12366cd6a6acSopenharmony_ci	}
12376cd6a6acSopenharmony_ci
12386cd6a6acSopenharmony_ci	paths[0] = pathname;
12396cd6a6acSopenharmony_ci
12406cd6a6acSopenharmony_ci	if (lstat(pathname, &sb) < 0) {
12416cd6a6acSopenharmony_ci		if (state.flags.ignore_noent && errno == ENOENT) {
12426cd6a6acSopenharmony_ci			free(pathdnamer);
12436cd6a6acSopenharmony_ci			free(pathname);
12446cd6a6acSopenharmony_ci			return 0;
12456cd6a6acSopenharmony_ci		} else {
12466cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
12476cd6a6acSopenharmony_ci				    "lstat(%s) failed: %m\n",
12486cd6a6acSopenharmony_ci				    pathname);
12496cd6a6acSopenharmony_ci			error = -1;
12506cd6a6acSopenharmony_ci			goto cleanup;
12516cd6a6acSopenharmony_ci		}
12526cd6a6acSopenharmony_ci	}
12536cd6a6acSopenharmony_ci
12546cd6a6acSopenharmony_ci	/* Skip digest if not a directory */
12556cd6a6acSopenharmony_ci	if (!S_ISDIR(sb.st_mode))
12566cd6a6acSopenharmony_ci		state.setrestorecondigest = false;
12576cd6a6acSopenharmony_ci
12586cd6a6acSopenharmony_ci	if (!state.flags.recurse) {
12596cd6a6acSopenharmony_ci		if (check_excluded(pathname)) {
12606cd6a6acSopenharmony_ci			error = 0;
12616cd6a6acSopenharmony_ci			goto cleanup;
12626cd6a6acSopenharmony_ci		}
12636cd6a6acSopenharmony_ci
12646cd6a6acSopenharmony_ci		error = restorecon_sb(pathname, &sb, &state.flags, true);
12656cd6a6acSopenharmony_ci		goto cleanup;
12666cd6a6acSopenharmony_ci	}
12676cd6a6acSopenharmony_ci
12686cd6a6acSopenharmony_ci	/* Obtain fs type */
12696cd6a6acSopenharmony_ci	memset(&state.sfsb, 0, sizeof(state.sfsb));
12706cd6a6acSopenharmony_ci	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) {
12716cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
12726cd6a6acSopenharmony_ci			    "statfs(%s) failed: %m\n",
12736cd6a6acSopenharmony_ci			    pathname);
12746cd6a6acSopenharmony_ci		error = -1;
12756cd6a6acSopenharmony_ci		goto cleanup;
12766cd6a6acSopenharmony_ci	}
12776cd6a6acSopenharmony_ci
12786cd6a6acSopenharmony_ci	/* Skip digest on in-memory filesystems and /sys */
12796cd6a6acSopenharmony_ci	if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC ||
12806cd6a6acSopenharmony_ci	    state.sfsb.f_type == SYSFS_MAGIC)
12816cd6a6acSopenharmony_ci		state.setrestorecondigest = false;
12826cd6a6acSopenharmony_ci
12836cd6a6acSopenharmony_ci	if (state.flags.set_xdev)
12846cd6a6acSopenharmony_ci		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
12856cd6a6acSopenharmony_ci	else
12866cd6a6acSopenharmony_ci		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
12876cd6a6acSopenharmony_ci
12886cd6a6acSopenharmony_ci	state.fts = fts_open(paths, fts_flags, NULL);
12896cd6a6acSopenharmony_ci	if (!state.fts)
12906cd6a6acSopenharmony_ci		goto fts_err;
12916cd6a6acSopenharmony_ci
12926cd6a6acSopenharmony_ci	state.ftsent_first = fts_read(state.fts);
12936cd6a6acSopenharmony_ci	if (!state.ftsent_first)
12946cd6a6acSopenharmony_ci		goto fts_err;
12956cd6a6acSopenharmony_ci
12966cd6a6acSopenharmony_ci	/*
12976cd6a6acSopenharmony_ci	 * Keep the inode of the first device. This is because the FTS_XDEV
12986cd6a6acSopenharmony_ci	 * flag tells fts not to descend into directories with different
12996cd6a6acSopenharmony_ci	 * device numbers, but fts will still give back the actual directory.
13006cd6a6acSopenharmony_ci	 * By saving the device number of the directory that was passed to
13016cd6a6acSopenharmony_ci	 * selinux_restorecon() and then skipping all actions on any
13026cd6a6acSopenharmony_ci	 * directories with a different device number when the FTS_XDEV flag
13036cd6a6acSopenharmony_ci	 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
13046cd6a6acSopenharmony_ci	 */
13056cd6a6acSopenharmony_ci	state.dev_num = state.ftsent_first->fts_statp->st_dev;
13066cd6a6acSopenharmony_ci
13076cd6a6acSopenharmony_ci	bool load_ignore_path = load_ignore_cfg();
13086cd6a6acSopenharmony_ci	if (!load_ignore_path) {
13096cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR, "Failed to load ignore cfg!\n");
13106cd6a6acSopenharmony_ci	}
13116cd6a6acSopenharmony_ci
13126cd6a6acSopenharmony_ci	if (nthreads == 1) {
13136cd6a6acSopenharmony_ci		state.parallel = false;
13146cd6a6acSopenharmony_ci		selinux_restorecon_thread(&state);
13156cd6a6acSopenharmony_ci	} else {
13166cd6a6acSopenharmony_ci		size_t i;
13176cd6a6acSopenharmony_ci		pthread_t self = pthread_self();
13186cd6a6acSopenharmony_ci		pthread_t *threads = NULL;
13196cd6a6acSopenharmony_ci
13206cd6a6acSopenharmony_ci		pthread_mutex_init(&state.mutex, NULL);
13216cd6a6acSopenharmony_ci
13226cd6a6acSopenharmony_ci		threads = calloc(nthreads - 1, sizeof(*threads));
13236cd6a6acSopenharmony_ci		if (!threads)
13246cd6a6acSopenharmony_ci			goto oom;
13256cd6a6acSopenharmony_ci
13266cd6a6acSopenharmony_ci		state.parallel = true;
13276cd6a6acSopenharmony_ci		/*
13286cd6a6acSopenharmony_ci		 * Start (nthreads - 1) threads - the main thread is going to
13296cd6a6acSopenharmony_ci		 * take part, too.
13306cd6a6acSopenharmony_ci		 */
13316cd6a6acSopenharmony_ci		for (i = 0; i < nthreads - 1; i++) {
13326cd6a6acSopenharmony_ci			if (pthread_create(&threads[i], NULL,
13336cd6a6acSopenharmony_ci					   selinux_restorecon_thread, &state)) {
13346cd6a6acSopenharmony_ci				/*
13356cd6a6acSopenharmony_ci				 * If any thread fails to be created, just mark
13366cd6a6acSopenharmony_ci				 * it as such and let the successfully created
13376cd6a6acSopenharmony_ci				 * threads do the job. In the worst case the
13386cd6a6acSopenharmony_ci				 * main thread will do everything, but that's
13396cd6a6acSopenharmony_ci				 * still better than to give up.
13406cd6a6acSopenharmony_ci				 */
13416cd6a6acSopenharmony_ci				threads[i] = self;
13426cd6a6acSopenharmony_ci			}
13436cd6a6acSopenharmony_ci		}
13446cd6a6acSopenharmony_ci
13456cd6a6acSopenharmony_ci		/* Let's join in on the fun! */
13466cd6a6acSopenharmony_ci		selinux_restorecon_thread(&state);
13476cd6a6acSopenharmony_ci
13486cd6a6acSopenharmony_ci		/* Now wait for all threads to finish. */
13496cd6a6acSopenharmony_ci		for (i = 0; i < nthreads - 1; i++) {
13506cd6a6acSopenharmony_ci			/* Skip threads that failed to be created. */
13516cd6a6acSopenharmony_ci			if (pthread_equal(threads[i], self))
13526cd6a6acSopenharmony_ci				continue;
13536cd6a6acSopenharmony_ci			pthread_join(threads[i], NULL);
13546cd6a6acSopenharmony_ci		}
13556cd6a6acSopenharmony_ci		free(threads);
13566cd6a6acSopenharmony_ci
13576cd6a6acSopenharmony_ci		pthread_mutex_destroy(&state.mutex);
13586cd6a6acSopenharmony_ci	}
13596cd6a6acSopenharmony_ci
13606cd6a6acSopenharmony_ci	error = state.error;
13616cd6a6acSopenharmony_ci	if (state.saved_errno)
13626cd6a6acSopenharmony_ci		goto out;
13636cd6a6acSopenharmony_ci
13646cd6a6acSopenharmony_ci	/*
13656cd6a6acSopenharmony_ci	 * Labeling successful. Write partial match digests for subdirectories.
13666cd6a6acSopenharmony_ci	 * TODO: Write digest upon FTS_DP if no error occurs in its descents.
13676cd6a6acSopenharmony_ci	 * Note: we can't ignore errors here that we've masked due to
13686cd6a6acSopenharmony_ci	 * SELINUX_RESTORECON_COUNT_ERRORS.
13696cd6a6acSopenharmony_ci	 */
13706cd6a6acSopenharmony_ci	if (state.setrestorecondigest && !state.flags.nochange && !error &&
13716cd6a6acSopenharmony_ci	    state.skipped_errors == 0) {
13726cd6a6acSopenharmony_ci		current = state.head;
13736cd6a6acSopenharmony_ci		while (current != NULL) {
13746cd6a6acSopenharmony_ci			if (setxattr(current->path,
13756cd6a6acSopenharmony_ci			    RESTORECON_PARTIAL_MATCH_DIGEST,
13766cd6a6acSopenharmony_ci			    current->digest,
13776cd6a6acSopenharmony_ci			    SHA1_HASH_SIZE, 0) < 0) {
13786cd6a6acSopenharmony_ci				selinux_log(SELINUX_ERROR,
13796cd6a6acSopenharmony_ci					    "setxattr failed: %s: %m\n",
13806cd6a6acSopenharmony_ci					    current->path);
13816cd6a6acSopenharmony_ci			}
13826cd6a6acSopenharmony_ci			current = current->next;
13836cd6a6acSopenharmony_ci		}
13846cd6a6acSopenharmony_ci	}
13856cd6a6acSopenharmony_ci
13866cd6a6acSopenharmony_ci	skipped_errors = state.skipped_errors;
13876cd6a6acSopenharmony_ci
13886cd6a6acSopenharmony_ciout:
13896cd6a6acSopenharmony_ci	if (state.flags.progress && state.flags.mass_relabel)
13906cd6a6acSopenharmony_ci		fprintf(stdout, "\r%s 100.0%%\n", pathname);
13916cd6a6acSopenharmony_ci
13926cd6a6acSopenharmony_ci	(void) fts_close(state.fts);
13936cd6a6acSopenharmony_ci	errno = state.saved_errno;
13946cd6a6acSopenharmony_cicleanup:
13956cd6a6acSopenharmony_ci	if (state.flags.add_assoc) {
13966cd6a6acSopenharmony_ci		if (state.flags.verbose)
13976cd6a6acSopenharmony_ci			filespec_eval();
13986cd6a6acSopenharmony_ci		filespec_destroy();
13996cd6a6acSopenharmony_ci	}
14006cd6a6acSopenharmony_ci	free(pathdnamer);
14016cd6a6acSopenharmony_ci	free(pathname);
14026cd6a6acSopenharmony_ci
14036cd6a6acSopenharmony_ci	current = state.head;
14046cd6a6acSopenharmony_ci	while (current != NULL) {
14056cd6a6acSopenharmony_ci		struct dir_hash_node *next = current->next;
14066cd6a6acSopenharmony_ci
14076cd6a6acSopenharmony_ci		free(current->path);
14086cd6a6acSopenharmony_ci		free(current);
14096cd6a6acSopenharmony_ci		current = next;
14106cd6a6acSopenharmony_ci	}
14116cd6a6acSopenharmony_ci	return error;
14126cd6a6acSopenharmony_ci
14136cd6a6acSopenharmony_cioom:
14146cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
14156cd6a6acSopenharmony_ci	error = -1;
14166cd6a6acSopenharmony_ci	goto cleanup;
14176cd6a6acSopenharmony_ci
14186cd6a6acSopenharmony_cirealpatherr:
14196cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR,
14206cd6a6acSopenharmony_ci		    "SELinux: Could not get canonical path for %s restorecon: %m.\n",
14216cd6a6acSopenharmony_ci		    pathname_orig);
14226cd6a6acSopenharmony_ci	error = -1;
14236cd6a6acSopenharmony_ci	goto cleanup;
14246cd6a6acSopenharmony_ci
14256cd6a6acSopenharmony_cifts_err:
14266cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR,
14276cd6a6acSopenharmony_ci		    "fts error while labeling %s: %m\n",
14286cd6a6acSopenharmony_ci		    paths[0]);
14296cd6a6acSopenharmony_ci	error = -1;
14306cd6a6acSopenharmony_ci	goto cleanup;
14316cd6a6acSopenharmony_ci}
14326cd6a6acSopenharmony_ci
14336cd6a6acSopenharmony_ci
14346cd6a6acSopenharmony_ci/*
14356cd6a6acSopenharmony_ci * Public API
14366cd6a6acSopenharmony_ci */
14376cd6a6acSopenharmony_ci
14386cd6a6acSopenharmony_ci/* selinux_restorecon(3) - Main function that is responsible for labeling */
14396cd6a6acSopenharmony_ciint selinux_restorecon(const char *pathname_orig,
14406cd6a6acSopenharmony_ci		       unsigned int restorecon_flags)
14416cd6a6acSopenharmony_ci{
14426cd6a6acSopenharmony_ci	return selinux_restorecon_common(pathname_orig, restorecon_flags, 1);
14436cd6a6acSopenharmony_ci}
14446cd6a6acSopenharmony_ci
14456cd6a6acSopenharmony_ci/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */
14466cd6a6acSopenharmony_ciint selinux_restorecon_parallel(const char *pathname_orig,
14476cd6a6acSopenharmony_ci				unsigned int restorecon_flags,
14486cd6a6acSopenharmony_ci				size_t nthreads)
14496cd6a6acSopenharmony_ci{
14506cd6a6acSopenharmony_ci	return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads);
14516cd6a6acSopenharmony_ci}
14526cd6a6acSopenharmony_ci
14536cd6a6acSopenharmony_ci/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
14546cd6a6acSopenharmony_civoid selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
14556cd6a6acSopenharmony_ci{
14566cd6a6acSopenharmony_ci	char **specfiles;
14576cd6a6acSopenharmony_ci	unsigned char *fc_digest;
14586cd6a6acSopenharmony_ci	size_t num_specfiles, fc_digest_len;
14596cd6a6acSopenharmony_ci
14606cd6a6acSopenharmony_ci	fc_sehandle = hndl;
14616cd6a6acSopenharmony_ci	if (!fc_sehandle)
14626cd6a6acSopenharmony_ci		return;
14636cd6a6acSopenharmony_ci
14646cd6a6acSopenharmony_ci	/* Check if digest requested in selabel_open(3), if so use it. */
14656cd6a6acSopenharmony_ci	if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
14666cd6a6acSopenharmony_ci				   &specfiles, &num_specfiles) < 0)
14676cd6a6acSopenharmony_ci		selabel_no_digest = true;
14686cd6a6acSopenharmony_ci	else
14696cd6a6acSopenharmony_ci		selabel_no_digest = false;
14706cd6a6acSopenharmony_ci}
14716cd6a6acSopenharmony_ci
14726cd6a6acSopenharmony_ci
14736cd6a6acSopenharmony_ci/*
14746cd6a6acSopenharmony_ci * selinux_restorecon_default_handle(3) is called to set the global restorecon
14756cd6a6acSopenharmony_ci * handle by a process if the default params are required.
14766cd6a6acSopenharmony_ci */
14776cd6a6acSopenharmony_cistruct selabel_handle *selinux_restorecon_default_handle(void)
14786cd6a6acSopenharmony_ci{
14796cd6a6acSopenharmony_ci	struct selabel_handle *sehandle;
14806cd6a6acSopenharmony_ci
14816cd6a6acSopenharmony_ci	struct selinux_opt fc_opts[] = {
14826cd6a6acSopenharmony_ci		{ SELABEL_OPT_DIGEST, (char *)1 }
14836cd6a6acSopenharmony_ci	};
14846cd6a6acSopenharmony_ci
14856cd6a6acSopenharmony_ci	sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
14866cd6a6acSopenharmony_ci
14876cd6a6acSopenharmony_ci	if (!sehandle) {
14886cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
14896cd6a6acSopenharmony_ci			    "Error obtaining file context handle: %m\n");
14906cd6a6acSopenharmony_ci		return NULL;
14916cd6a6acSopenharmony_ci	}
14926cd6a6acSopenharmony_ci
14936cd6a6acSopenharmony_ci	selabel_no_digest = false;
14946cd6a6acSopenharmony_ci	return sehandle;
14956cd6a6acSopenharmony_ci}
14966cd6a6acSopenharmony_ci
14976cd6a6acSopenharmony_ci/*
14986cd6a6acSopenharmony_ci * selinux_restorecon_set_exclude_list(3) is called to add additional entries
14996cd6a6acSopenharmony_ci * to be excluded from labeling checks.
15006cd6a6acSopenharmony_ci */
15016cd6a6acSopenharmony_civoid selinux_restorecon_set_exclude_list(const char **exclude_list)
15026cd6a6acSopenharmony_ci{
15036cd6a6acSopenharmony_ci	int i;
15046cd6a6acSopenharmony_ci	struct stat sb;
15056cd6a6acSopenharmony_ci
15066cd6a6acSopenharmony_ci	for (i = 0; exclude_list[i]; i++) {
15076cd6a6acSopenharmony_ci		if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) {
15086cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
15096cd6a6acSopenharmony_ci				    "lstat error on exclude path \"%s\", %m - ignoring.\n",
15106cd6a6acSopenharmony_ci				    exclude_list[i]);
15116cd6a6acSopenharmony_ci			break;
15126cd6a6acSopenharmony_ci		}
15136cd6a6acSopenharmony_ci		if (add_exclude(exclude_list[i], CALLER_EXCLUDED) &&
15146cd6a6acSopenharmony_ci		    errno == ENOMEM)
15156cd6a6acSopenharmony_ci			assert(0);
15166cd6a6acSopenharmony_ci	}
15176cd6a6acSopenharmony_ci}
15186cd6a6acSopenharmony_ci
15196cd6a6acSopenharmony_ci/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
15206cd6a6acSopenharmony_ciint selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
15216cd6a6acSopenharmony_ci{
15226cd6a6acSopenharmony_ci	size_t len;
15236cd6a6acSopenharmony_ci
15246cd6a6acSopenharmony_ci	/* This should be NULL on first use */
15256cd6a6acSopenharmony_ci	if (rootpath)
15266cd6a6acSopenharmony_ci		free(rootpath);
15276cd6a6acSopenharmony_ci
15286cd6a6acSopenharmony_ci	rootpath = strdup(alt_rootpath);
15296cd6a6acSopenharmony_ci	if (!rootpath) {
15306cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
15316cd6a6acSopenharmony_ci		return -1;
15326cd6a6acSopenharmony_ci	}
15336cd6a6acSopenharmony_ci
15346cd6a6acSopenharmony_ci	/* trim trailing /, if present */
15356cd6a6acSopenharmony_ci	len = strlen(rootpath);
15366cd6a6acSopenharmony_ci	while (len && (rootpath[len - 1] == '/'))
15376cd6a6acSopenharmony_ci		rootpath[--len] = '\0';
15386cd6a6acSopenharmony_ci	rootpathlen = len;
15396cd6a6acSopenharmony_ci
15406cd6a6acSopenharmony_ci	return 0;
15416cd6a6acSopenharmony_ci}
15426cd6a6acSopenharmony_ci
15436cd6a6acSopenharmony_ci/* selinux_restorecon_xattr(3)
15446cd6a6acSopenharmony_ci * Find RESTORECON_PARTIAL_MATCH_DIGEST entries.
15456cd6a6acSopenharmony_ci */
15466cd6a6acSopenharmony_ciint selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
15476cd6a6acSopenharmony_ci			     struct dir_xattr ***xattr_list)
15486cd6a6acSopenharmony_ci{
15496cd6a6acSopenharmony_ci	bool recurse = (xattr_flags &
15506cd6a6acSopenharmony_ci	    SELINUX_RESTORECON_XATTR_RECURSE) ? true : false;
15516cd6a6acSopenharmony_ci	bool delete_nonmatch = (xattr_flags &
15526cd6a6acSopenharmony_ci	    SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false;
15536cd6a6acSopenharmony_ci	bool delete_all = (xattr_flags &
15546cd6a6acSopenharmony_ci	    SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false;
15556cd6a6acSopenharmony_ci	ignore_mounts = (xattr_flags &
15566cd6a6acSopenharmony_ci	   SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false;
15576cd6a6acSopenharmony_ci
15586cd6a6acSopenharmony_ci	int rc, fts_flags;
15596cd6a6acSopenharmony_ci	struct stat sb;
15606cd6a6acSopenharmony_ci	struct statfs sfsb;
15616cd6a6acSopenharmony_ci	struct dir_xattr *current, *next;
15626cd6a6acSopenharmony_ci	FTS *fts;
15636cd6a6acSopenharmony_ci	FTSENT *ftsent;
15646cd6a6acSopenharmony_ci	char *paths[2] = { NULL, NULL };
15656cd6a6acSopenharmony_ci
15666cd6a6acSopenharmony_ci	__selinux_once(fc_once, restorecon_init);
15676cd6a6acSopenharmony_ci
15686cd6a6acSopenharmony_ci	if (!fc_sehandle)
15696cd6a6acSopenharmony_ci		return -1;
15706cd6a6acSopenharmony_ci
15716cd6a6acSopenharmony_ci	if (lstat(pathname, &sb) < 0) {
15726cd6a6acSopenharmony_ci		if (errno == ENOENT)
15736cd6a6acSopenharmony_ci			return 0;
15746cd6a6acSopenharmony_ci
15756cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
15766cd6a6acSopenharmony_ci			    "lstat(%s) failed: %m\n",
15776cd6a6acSopenharmony_ci			    pathname);
15786cd6a6acSopenharmony_ci		return -1;
15796cd6a6acSopenharmony_ci	}
15806cd6a6acSopenharmony_ci
15816cd6a6acSopenharmony_ci	if (!recurse) {
15826cd6a6acSopenharmony_ci		if (statfs(pathname, &sfsb) == 0) {
15836cd6a6acSopenharmony_ci			if (sfsb.f_type == RAMFS_MAGIC ||
15846cd6a6acSopenharmony_ci			    sfsb.f_type == TMPFS_MAGIC)
15856cd6a6acSopenharmony_ci				return 0;
15866cd6a6acSopenharmony_ci		}
15876cd6a6acSopenharmony_ci
15886cd6a6acSopenharmony_ci		if (check_excluded(pathname))
15896cd6a6acSopenharmony_ci			return 0;
15906cd6a6acSopenharmony_ci
15916cd6a6acSopenharmony_ci		rc = add_xattr_entry(pathname, delete_nonmatch, delete_all);
15926cd6a6acSopenharmony_ci
15936cd6a6acSopenharmony_ci		if (!rc && dir_xattr_list)
15946cd6a6acSopenharmony_ci			*xattr_list = &dir_xattr_list;
15956cd6a6acSopenharmony_ci		else if (rc == -1)
15966cd6a6acSopenharmony_ci			return rc;
15976cd6a6acSopenharmony_ci
15986cd6a6acSopenharmony_ci		return 0;
15996cd6a6acSopenharmony_ci	}
16006cd6a6acSopenharmony_ci
16016cd6a6acSopenharmony_ci	paths[0] = (char *)pathname;
16026cd6a6acSopenharmony_ci	fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
16036cd6a6acSopenharmony_ci
16046cd6a6acSopenharmony_ci	fts = fts_open(paths, fts_flags, NULL);
16056cd6a6acSopenharmony_ci	if (!fts) {
16066cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
16076cd6a6acSopenharmony_ci			    "fts error on %s: %m\n",
16086cd6a6acSopenharmony_ci			    paths[0]);
16096cd6a6acSopenharmony_ci		return -1;
16106cd6a6acSopenharmony_ci	}
16116cd6a6acSopenharmony_ci
16126cd6a6acSopenharmony_ci	while ((ftsent = fts_read(fts)) != NULL) {
16136cd6a6acSopenharmony_ci		switch (ftsent->fts_info) {
16146cd6a6acSopenharmony_ci		case FTS_DP:
16156cd6a6acSopenharmony_ci			continue;
16166cd6a6acSopenharmony_ci		case FTS_D:
16176cd6a6acSopenharmony_ci			if (statfs(ftsent->fts_path, &sfsb) == 0) {
16186cd6a6acSopenharmony_ci				if (sfsb.f_type == RAMFS_MAGIC ||
16196cd6a6acSopenharmony_ci				    sfsb.f_type == TMPFS_MAGIC)
16206cd6a6acSopenharmony_ci					continue;
16216cd6a6acSopenharmony_ci			}
16226cd6a6acSopenharmony_ci			if (check_excluded(ftsent->fts_path)) {
16236cd6a6acSopenharmony_ci				fts_set(fts, ftsent, FTS_SKIP);
16246cd6a6acSopenharmony_ci				continue;
16256cd6a6acSopenharmony_ci			}
16266cd6a6acSopenharmony_ci
16276cd6a6acSopenharmony_ci			rc = add_xattr_entry(ftsent->fts_path,
16286cd6a6acSopenharmony_ci					     delete_nonmatch, delete_all);
16296cd6a6acSopenharmony_ci			if (rc == 1)
16306cd6a6acSopenharmony_ci				continue;
16316cd6a6acSopenharmony_ci			else if (rc == -1)
16326cd6a6acSopenharmony_ci				goto cleanup;
16336cd6a6acSopenharmony_ci			break;
16346cd6a6acSopenharmony_ci		default:
16356cd6a6acSopenharmony_ci			break;
16366cd6a6acSopenharmony_ci		}
16376cd6a6acSopenharmony_ci	}
16386cd6a6acSopenharmony_ci
16396cd6a6acSopenharmony_ci	if (dir_xattr_list)
16406cd6a6acSopenharmony_ci		*xattr_list = &dir_xattr_list;
16416cd6a6acSopenharmony_ci
16426cd6a6acSopenharmony_ci	(void) fts_close(fts);
16436cd6a6acSopenharmony_ci	return 0;
16446cd6a6acSopenharmony_ci
16456cd6a6acSopenharmony_cicleanup:
16466cd6a6acSopenharmony_ci	rc = errno;
16476cd6a6acSopenharmony_ci	(void) fts_close(fts);
16486cd6a6acSopenharmony_ci	errno = rc;
16496cd6a6acSopenharmony_ci
16506cd6a6acSopenharmony_ci	if (dir_xattr_list) {
16516cd6a6acSopenharmony_ci		/* Free any used memory */
16526cd6a6acSopenharmony_ci		current = dir_xattr_list;
16536cd6a6acSopenharmony_ci		while (current) {
16546cd6a6acSopenharmony_ci			next = current->next;
16556cd6a6acSopenharmony_ci			free(current->directory);
16566cd6a6acSopenharmony_ci			free(current->digest);
16576cd6a6acSopenharmony_ci			free(current);
16586cd6a6acSopenharmony_ci			current = next;
16596cd6a6acSopenharmony_ci		}
16606cd6a6acSopenharmony_ci	}
16616cd6a6acSopenharmony_ci	return -1;
16626cd6a6acSopenharmony_ci}
16636cd6a6acSopenharmony_ci
16646cd6a6acSopenharmony_cilong unsigned selinux_restorecon_get_skipped_errors(void)
16656cd6a6acSopenharmony_ci{
16666cd6a6acSopenharmony_ci	return skipped_errors;
16676cd6a6acSopenharmony_ci}
1668