diff --git a/policy/Makefile b/policy/Makefile
index cc70d33..a8c8b8a 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -25,7 +25,7 @@ TARGETS = \
 	test_task_getsid.te test_task_setpgid.te test_task_setsched.te \
 	test_transition.te test_inet_socket.te test_unix_socket.te \
 	test_mmap.te test_overlayfs.te test_mqueue.te test_mac_admin.te \
-	test_ibpkey.te test_atsecure.te
+	test_ibpkey.te test_atsecure.te test_restorecon.te
 
 ifeq ($(shell [ $(POL_VERS) -ge 24 ] && echo true),true)
 TARGETS += test_bounds.te
diff --git a/policy/test_restorecon.te b/policy/test_restorecon.te
new file mode 100644
index 0000000..91cc6ea
--- /dev/null
+++ b/policy/test_restorecon.te
@@ -0,0 +1,40 @@
+#################################
+#
+# Policy for testing restorecon
+#
+
+require {
+	attribute file_type;
+}
+
+attribute restorecon_domain;
+
+type test_restorecon_file_t;
+files_type(test_restorecon_file_t)
+type in_dir_t;
+files_type(in_dir_t)
+type out_dir_t;
+files_type(out_dir_t)
+type in_file_t;
+files_type(in_file_t)
+type out_file_t;
+files_type(out_file_t)
+
+# Domain for process that can restorecon the test file.
+type test_restorecon_t;
+files_type(test_restorecon_t)
+
+domain_type(test_restorecon_t)
+unconfined_runs_test(test_restorecon_t)
+typeattribute test_restorecon_t testdomain;
+typeattribute test_restorecon_t restorecon_domain;
+
+allow test_restorecon_t self:capability sys_admin;
+allow test_restorecon_t test_file_t:file relabelfrom;
+allow test_restorecon_t file_type:dir { relabel_dir_perms manage_dir_perms };
+allow test_restorecon_t file_type:file { rw_file_perms execute relabelto relabelfrom };
+allow_map(test_restorecon_t, file_type, file)
+
+# Allow all of these domains to be entered from sysadm domain
+miscfiles_domain_entry_test_files(restorecon_domain)
+userdom_sysadm_entry_spec_domtrans_to(restorecon_domain)
diff --git a/tests/Makefile b/tests/Makefile
index fb6de5c..94eca9f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -47,6 +47,10 @@ ifeq ($(shell grep "^SELINUX_INFINIBAND_PKEY_TEST=" infiniband_pkey/ibpkey_test.
 SUBDIRS += infiniband_pkey
 endif
 
+ifeq ($(shell grep -q selabel_get_digests_all_partial_matches $(INCLUDEDIR)/selinux/label.h && echo true),true)
+SUBDIRS += restorecon
+endif
+
 ifeq ($(DISTRO),RHEL4)
     SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
 endif
diff --git a/tests/restorecon/.gitignore b/tests/restorecon/.gitignore
new file mode 100644
index 0000000..c37f923
--- /dev/null
+++ b/tests/restorecon/.gitignore
@@ -0,0 +1,2 @@
+selinux_restorecon
+get_digests
diff --git a/tests/restorecon/Makefile b/tests/restorecon/Makefile
new file mode 100644
index 0000000..5c0d6e2
--- /dev/null
+++ b/tests/restorecon/Makefile
@@ -0,0 +1,8 @@
+TARGETS = selinux_restorecon get_digests
+
+LDLIBS += -lselinux
+
+all: $(TARGETS)
+
+clean:
+	rm -f $(TARGETS)
diff --git a/tests/restorecon/get_digests.c b/tests/restorecon/get_digests.c
new file mode 100644
index 0000000..875ba3b
--- /dev/null
+++ b/tests/restorecon/get_digests.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+#define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
+
+static void usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-v] path\n\n"
+		"Where:\n\t"
+		"-v  Display information.\n\t"
+		"path  Path to check current SHA1 digest against file_contexts entries.\n\n"
+		"This will check the directory selinux.sehash SHA1 digest for "
+		"<path> against\na newly generated digest based on the "
+		"file_context entries for that node\n(using the regx, mode "
+		"and path entries).\n", progname);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	int opt, rc = 0; /* The hashes do NOT match */
+	size_t i, digest_len = 0;
+	bool status, verbose = false;
+	uint8_t *xattr_digest = NULL;
+	uint8_t *calculated_digest = NULL;
+	char *sha1_buf = NULL;
+
+	struct selabel_handle *hnd;
+
+	if (argc < 2)
+		usage(argv[0]);
+
+	while ((opt = getopt(argc, argv, "v")) > 0) {
+		switch (opt) {
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "No pathname specified\n");
+		exit(-1);
+	}
+
+	hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+	if (!hnd) {
+		fprintf(stderr, "ERROR: selabel_open - Could not obtain handle.\n");
+		return -1;
+	}
+
+	status = selabel_get_digests_all_partial_matches(hnd, argv[optind],
+							 &calculated_digest,
+							 &xattr_digest,
+							 &digest_len);
+
+	sha1_buf = calloc(1, digest_len * 2 + 1);
+	if (!sha1_buf) {
+		fprintf(stderr, "Could not calloc buffer ERROR: %s\n",
+			strerror(errno));
+		rc = -1;
+		goto out;
+	}
+
+	if (status) { /* They match */
+		if (verbose) {
+			printf("xattr and file_contexts SHA1 digests match for: %s\n",
+			       argv[optind]);
+
+			if (calculated_digest) {
+				for (i = 0; i < digest_len; i++)
+					sprintf((&sha1_buf[i * 2]), "%02x",
+						calculated_digest[i]);
+				printf("SHA1 digest: %s\n", sha1_buf);
+			}
+		}
+
+		rc = 1;
+		goto out;
+	} else {
+		if (!calculated_digest) {
+			rc = 2;
+			if (verbose) {
+				printf("No SHA1 digest available for: %s\n", argv[optind]);
+				printf("as file_context entry is \"<<none>>\"\n");
+			}
+		}
+
+		if (calculated_digest && verbose) {
+			printf("The file_context entries for: %s\n", argv[optind]);
+
+			for (i = 0; i < digest_len; i++)
+				sprintf((&sha1_buf[i * 2]), "%02x", calculated_digest[i]);
+			printf("generated SHA1 digest: %s\n", sha1_buf);
+		}
+		if (!xattr_digest) {
+			rc = rc | 4;
+			if (verbose)
+				printf("however there is no selinux.sehash xattr entry.\n");
+			else
+				goto out;
+
+		} else if (verbose) {
+			printf("however it does NOT match the current entry of:\n");
+			for (i = 0; i < digest_len; i++)
+				sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]);
+			printf("%s\n", sha1_buf);
+		}
+	}
+out:
+	selabel_close(hnd);
+	free(xattr_digest);
+	free(calculated_digest);
+	free(sha1_buf);
+	return rc;
+}
diff --git a/tests/restorecon/selinux_restorecon.c b/tests/restorecon/selinux_restorecon.c
new file mode 100644
index 0000000..7ea9387
--- /dev/null
+++ b/tests/restorecon/selinux_restorecon.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/restorecon.h>
+
+static void usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-vr]\n"
+		"Where:\n\t"
+		"-v  Display information.\n\t"
+		"-r  Recursively descend directories.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+	int opt, rc, flags = 0;
+	bool verbose = false;
+
+	struct selabel_handle *hnd;
+
+	if (argc < 2)
+		usage(argv[0]);
+
+	while ((opt = getopt(argc, argv, "rv")) > 0) {
+		switch (opt) {
+		case 'r':
+			flags = SELINUX_RESTORECON_RECURSE;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "No pathname specified\n");
+		exit(-1);
+	}
+
+	hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+	if (!hnd) {
+		fprintf(stderr, "ERROR: selabel_open - Could not obtain handle.\n");
+		return -1;
+	}
+
+	/* Use own handle */
+	selinux_restorecon_set_sehandle(hnd);
+
+	if (verbose)
+		flags |= SELINUX_RESTORECON_VERBOSE;
+
+	rc = selinux_restorecon(argv[optind], flags);
+	if (rc < 0)
+		fprintf(stderr, "selinux_restorecon ERROR: %s\n",
+			strerror(errno));
+
+	return rc;
+}
+
diff --git a/tests/restorecon/test b/tests/restorecon/test
new file mode 100755
index 0000000..208b6c4
--- /dev/null
+++ b/tests/restorecon/test
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    # allow info to be shown
+    $v = $ARGV[0];
+    if ($v) {
+        if ( $v ne "-v" ) {
+            plan skip_all => "Invalid option (use -v)";
+        }
+    }
+    else {
+        $v = " ";
+    }
+
+    plan tests => 6;
+}
+
+# Need to get full path for semanage and tests progs
+use Cwd qw(cwd);
+$cwd = cwd;
+if ( $basedir ne "." ) {
+    $path = "$cwd/$basedir";
+}
+else {
+    $path = $cwd;
+}
+
+# Make sure removed then generate new
+system("rm -rf $basedir/restore_test");
+system("mkdir -p $basedir/restore_test/in_dir");
+system("mkdir -p $basedir/restore_test/out_dir");
+
+system("semanage fcontext -a -t test_file_t -f d $path/restore_test");
+system("semanage fcontext -a -t in_dir_t -f d $path/restore_test/in_dir");
+system("semanage fcontext -a -t out_dir_t -f d $path/restore_test/out_dir");
+
+# Add some files
+system("touch $path/restore_test/out_dir/out_file1");
+system("touch $path/restore_test/in_dir/in_file1");
+
+# There is no selinux.sehash xattr entry
+$result =
+  system(
+    "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test");
+ok( $result >> 8 eq 4 );
+
+$result =
+  system(
+"runcon -t test_restorecon_t $basedir/selinux_restorecon -r $v $path/restore_test"
+  );
+ok( $result eq 0 );
+
+# After restorecon they match
+$result =
+  system(
+    "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test");
+ok( $result >> 8 eq 1 );
+
+# Add new file_context enties to get the files relabeled:
+system(
+    "semanage fcontext -a -t in_file_t -f f \"$path/restore_test/in_dir(/.*)?\""
+);
+system(
+"semanage fcontext -a -t out_file_t -f f \"$path/restore_test/out_dir(/.*)?\""
+);
+
+# Now the digests do NOT match
+$result =
+  system(
+    "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test");
+ok( $result eq 0 );
+
+$result =
+  system(
+"runcon -t test_restorecon_t $basedir/selinux_restorecon -r $v $path/restore_test"
+  );
+ok( $result eq 0 );
+
+# After restorecon they match again
+$result =
+  system(
+    "runcon -t test_restorecon_t $basedir/get_digests $v $path/restore_test");
+ok( $result >> 8 eq 1 );
+
+# semanage x 10 takes 50s. Using semodule loading 2 x *.cil + 1 delete is slower 1m:40s
+system("semanage fcontext -d -t in_dir_t -f d $path/restore_test/in_dir");
+system("semanage fcontext -d -t out_dir_t -f d $path/restore_test/out_dir");
+system(
+    "semanage fcontext -d -t in_file_t -f f \"$path/restore_test/in_dir(/.*)?\""
+);
+system(
+"semanage fcontext -d -t in_file_t -f f \"$path/restore_test/out_dir(/.*)?\""
+);
+system("semanage fcontext -d -t test_file_t -f d $path/restore_test");
+system("rm -rf $basedir/restore_test");
+
+exit;
