diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
index 1821a3dc596c..b10fe684eff9 100644
--- a/libselinux/include/selinux/restorecon.h
+++ b/libselinux/include/selinux/restorecon.h
@@ -121,6 +121,11 @@ extern int selinux_restorecon_parallel(const char *pathname,
  */
 #define SELINUX_RESTORECON_CONFLICT_ERROR		0x10000
 
+/*
+ * Count, but otherwise ignore, errors during the file tree walk.
+ */
+#define SELINUX_RESTORECON_COUNT_ERRORS			0x20000
+
 /**
  * selinux_restorecon_set_sehandle - Set the global fc handle.
  * @hndl: specifies handle to set as the global fc handle.
@@ -205,6 +210,16 @@ extern int selinux_restorecon_xattr(const char *pathname,
 /* Do not read /proc/mounts. */
 #define SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS			0x0008
 
+/* selinux_restorecon_get_skipped_errors - Get the number of errors ignored
+ * during re-labeling.
+ *
+ * If SELINUX_RESTORECON_COUNT_ERRORS was passed to selinux_restorecon(3) or
+ * selinux_restorecon_parallel(3), and that function returned successfully
+ * (i.e., with a zero return value), then this function returns the number of
+ * errors ignored during the file tree walk.
+ */
+extern long unsigned selinux_restorecon_get_skipped_errors(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
index 72f4fb462e34..e61929120a74 100644
--- a/libselinux/src/selinux_restorecon.c
+++ b/libselinux/src/selinux_restorecon.c
@@ -66,6 +66,9 @@ static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
 static struct dir_xattr *dir_xattr_list;
 static struct dir_xattr *dir_xattr_last;
 
+/* Number of errors ignored during the file tree walk. */
+static long unsigned skipped_errors;
+
 /* restorecon_flags for passing to restorecon_sb() */
 struct rest_flags {
 	bool nochange;
@@ -83,6 +86,7 @@ struct rest_flags {
 	bool ignore_noent;
 	bool warnonnomatch;
 	bool conflicterror;
+	bool count_errors;
 };
 
 static void restorecon_init(void)
@@ -827,6 +831,7 @@ struct rest_state {
 	struct dir_hash_node *head, *current;
 	bool abort;
 	int error;
+	long unsigned skipped_errors;
 	int saved_errno;
 	pthread_mutex_t mutex;
 };
@@ -949,11 +954,17 @@ loop_body:
 					goto unlock;
 			}
 
-			state->error |= error;
 			first = false;
-			if (error && state->flags.abort_on_error) {
-				state->abort = true;
-				goto finish;
+			if (error) {
+				if (state->flags.abort_on_error) {
+					state->error = error;
+					state->abort = true;
+					goto finish;
+				}
+				if (state->flags.count_errors)
+					state->skipped_errors++;
+				else
+					state->error = error;
 			}
 			break;
 		}
@@ -1007,12 +1018,15 @@ static int selinux_restorecon_common(const char *pathname_orig,
 		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
 	state.ignore_digest = (restorecon_flags &
 		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
+	state.flags.count_errors = (restorecon_flags &
+		    SELINUX_RESTORECON_COUNT_ERRORS) ? true : false;
 	state.setrestorecondigest = true;
 
 	state.head = NULL;
 	state.current = NULL;
 	state.abort = false;
 	state.error = 0;
+	state.skipped_errors = 0;
 	state.saved_errno = 0;
 
 	struct stat sb;
@@ -1225,8 +1239,11 @@ static int selinux_restorecon_common(const char *pathname_orig,
 	/*
 	 * Labeling successful. Write partial match digests for subdirectories.
 	 * TODO: Write digest upon FTS_DP if no error occurs in its descents.
+	 * Note: we can't ignore errors here that we've masked due to
+	 * SELINUX_RESTORECON_COUNT_ERRORS.
 	 */
-	if (state.setrestorecondigest && !state.flags.nochange && !error) {
+	if (state.setrestorecondigest && !state.flags.nochange && !error &&
+	    state.skipped_errors == 0) {
 		current = state.head;
 		while (current != NULL) {
 			if (setxattr(current->path,
@@ -1241,6 +1258,8 @@ static int selinux_restorecon_common(const char *pathname_orig,
 		}
 	}
 
+	skipped_errors = state.skipped_errors;
+
 out:
 	if (state.flags.progress && state.flags.mass_relabel)
 		fprintf(stdout, "\r%s 100.0%%\n", pathname);
@@ -1520,3 +1539,8 @@ cleanup:
 	}
 	return -1;
 }
+
+long unsigned selinux_restorecon_get_skipped_errors(void)
+{
+	return skipped_errors;
+}
diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3
index 334d2930bb4f..218aaf6d2ae5 100644
--- a/libselinux/man/man3/selinux_restorecon.3
+++ b/libselinux/man/man3/selinux_restorecon.3
@@ -78,7 +78,10 @@ specfile entries SHA1 digest. The specfile entries digest will be written to the
 .IR security.sehash
 extended attribute once relabeling has been completed successfully provided the
 .B SELINUX_RESTORECON_NOCHANGE
-flag has not been set.
+flag has not been set, and no errors have been skipped during the file tree walk
+due to the
+.B SELINUX_RESTORECON_COUNT_ERRORS
+flag.
 .sp
 .B SELINUX_RESTORECON_NOCHANGE
 don't change any file labels (passive check) or update the digest in the
@@ -164,6 +167,21 @@ on a directory below this.
 .B SELINUX_RESTORECON_CONFLICT_ERROR
 to treat conflicting specifications, such as where two hardlinks for the
 same inode have different contexts, as errors.
+.sp
+.B SELINUX_RESTORECON_COUNT_ERRORS
+Count, but otherwise ignore, errors during the file tree walk. Only makes a
+difference if the
+.B SELINUX_RESTORECON_ABORT_ON_ERROR
+flag is clear. Call
+.BR selinux_restorecon_get_skipped_errors (3)
+for fetching the ignored (skipped) error count after
+.BR selinux_restorecon (3)
+or
+.BR selinux_restorecon_parallel (3)
+completes with success. In case any errors were skipped during the file tree
+walk, the specfile entries SHA1 digest will not have been written to the
+.IR security.sehash
+extended attribute.
 .RE
 .sp
 The behavior regarding the checking and updating of the SHA1 digest described
@@ -279,6 +297,8 @@ option.
 .br
 .BR selinux_restorecon_default_handle (3),
 .br
+.BR selinux_restorecon_get_skipped_errors (3),
+.br
 .BR selinux_restorecon_set_exclude_list (3),
 .br
 .BR selinux_restorecon_set_alt_rootpath (3),
diff --git a/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3 b/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3
new file mode 100644
index 000000000000..d1757b7612ab
--- /dev/null
+++ b/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3
@@ -0,0 +1,28 @@
+.TH "selinux_restorecon_get_skipped_errors" "3" "27 Apr 2022" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_get_skipped_errors \- get the number of errors ignored by
+.BR selinux_restorecon (3)
+or
+.BR selinux_restorecon_parallel (3)
+during the file tree walk
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.sp
+.BI "long unsigned selinux_restorecon_get_skipped_errors(void);"
+.in +\w'long unsigned selinux_restorecon_get_skipped_errors('u
+.
+.SH "DESCRIPTION"
+If
+.B SELINUX_RESTORECON_COUNT_ERRORS
+was passed to
+.BR selinux_restorecon (3)
+or
+.BR selinux_restorecon_parallel (3)
+and that function returned successfully (i.e., with a zero return value), then
+.BR selinux_restorecon_get_skipped_errors ()
+returns the number of errors ignored during the file tree walk.
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon (3)
diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map
index 4acf1caacb55..43631f0965fe 100644
--- a/libselinux/src/libselinux.map
+++ b/libselinux/src/libselinux.map
@@ -245,3 +245,8 @@ LIBSELINUX_3.4 {
   global:
     selinux_restorecon_parallel;
 } LIBSELINUX_1.0;
+
+LIBSELINUX_3.5 {
+  global:
+    selinux_restorecon_get_skipped_errors;
+} LIBSELINUX_3.4;
