selinux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Vit Mojzis <vmojzis@redhat.com>
To: selinux@vger.kernel.org
Subject: [PATCH] restorecon: Add option to count number of relabeled files
Date: Mon, 10 Nov 2025 19:05:50 +0100	[thread overview]
Message-ID: <20251110180935.202229-1-vmojzis@redhat.com> (raw)

This is useful in case we want to check that a remediation using
restorecon was successful (otherwise 0 is always returned, even if no
files where relabeled).

Signed-off-by: Vit Mojzis <vmojzis@redhat.com>
---
 libselinux/include/selinux/restorecon.h |  9 +++++++++
 libselinux/src/libselinux.map           |  1 +
 libselinux/src/selinux_restorecon.c     | 14 ++++++++++++++
 policycoreutils/setfiles/restore.h      |  1 +
 policycoreutils/setfiles/restorecon.8   |  3 +++
 policycoreutils/setfiles/setfiles.c     | 24 ++++++++++++++++++++----
 6 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
index 0ccf73a6..736481bb 100644
--- a/libselinux/include/selinux/restorecon.h
+++ b/libselinux/include/selinux/restorecon.h
@@ -228,6 +228,15 @@ extern int selinux_restorecon_xattr(const char *pathname,
  */
 extern long unsigned selinux_restorecon_get_skipped_errors(void);
 
+/* selinux_restorecon_get_relabeled_files - Get the number of relabeled files
+ *
+ * If selinux_restorecon(3) or selinux_restorecon_parallel(3) was called,
+ * this function returns the number of files that were successfully relabeled.
+ * If the SELINUX_RESTORECON_NOCHANGE flag was set, this function returns
+ * the number of files that would be relabeled.
+ */
+extern long unsigned selinux_restorecon_get_relabeled_files(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map
index ab002f01..f21e089e 100644
--- a/libselinux/src/libselinux.map
+++ b/libselinux/src/libselinux.map
@@ -244,6 +244,7 @@ LIBSELINUX_1.0 {
 LIBSELINUX_3.4 {
   global:
     selinux_restorecon_get_skipped_errors;
+    selinux_restorecon_get_relabeled_files;
     selinux_restorecon_parallel;
 } LIBSELINUX_1.0;
 
diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
index 681c69db..6e9a159e 100644
--- a/libselinux/src/selinux_restorecon.c
+++ b/libselinux/src/selinux_restorecon.c
@@ -69,6 +69,9 @@ static struct dir_xattr *dir_xattr_last;
 /* Number of errors ignored during the file tree walk. */
 static long unsigned skipped_errors;
 
+/* Number of successfully relabeled files or files that would be relabeled */
+static long unsigned relabeled_files;
+
 /* restorecon_flags for passing to restorecon_sb() */
 struct rest_flags {
 	bool nochange;
@@ -796,6 +799,10 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
 				syslog(LOG_INFO, "labeling %s to %s\n",
 					    pathname, newcon);
 		}
+
+		/* Count relabeled files (or would be relabeled if "nochange" was not set) */
+		relabeled_files++;
+
 	}
 
 out:
@@ -1096,6 +1103,8 @@ static int selinux_restorecon_common(const char *pathname_orig,
 	state.skipped_errors = 0;
 	state.saved_errno = 0;
 
+	relabeled_files = 0;
+
 	struct stat sb;
 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
 	char *paths[2] = { NULL, NULL };
@@ -1618,3 +1627,8 @@ long unsigned selinux_restorecon_get_skipped_errors(void)
 {
 	return skipped_errors;
 }
+
+long unsigned selinux_restorecon_get_relabeled_files(void)
+{
+	return relabeled_files;
+}
diff --git a/policycoreutils/setfiles/restore.h b/policycoreutils/setfiles/restore.h
index 95afb960..7c949c1c 100644
--- a/policycoreutils/setfiles/restore.h
+++ b/policycoreutils/setfiles/restore.h
@@ -37,6 +37,7 @@ struct restore_opts {
 	unsigned int ignore_mounts;
 	unsigned int conflict_error;
 	unsigned int count_errors;
+	unsigned int count_relabeled;
 	/* restorecon_flags holds | of above for restore_init() */
 	unsigned int restorecon_flags;
 	char *rootpath;
diff --git a/policycoreutils/setfiles/restorecon.8 b/policycoreutils/setfiles/restorecon.8
index 1134420e..e9bd16b6 100644
--- a/policycoreutils/setfiles/restorecon.8
+++ b/policycoreutils/setfiles/restorecon.8
@@ -153,6 +153,9 @@ display warnings about entries that had no matching files by outputting the
 .BR selabel_stats (3)
 results.
 .TP
+.B \-c
+count and display the number of (would be) relabeled files. The exit code will be set to the number of relabeled files (capped at 254).
+.TP
 .B \-0
 the separator for the input items is assumed to be the null character
 (instead of the white space).  The quotes and the backslash characters are
diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c
index 31034316..6323f56c 100644
--- a/policycoreutils/setfiles/setfiles.c
+++ b/policycoreutils/setfiles/setfiles.c
@@ -35,8 +35,8 @@ static __attribute__((__noreturn__)) void usage(const char *const name)
 {
 	if (iamrestorecon) {
 		fprintf(stderr,
-			"usage:  %s [-iIDFUmnprRv0xT] [-e excludedir] pathname...\n"
-			"usage:  %s [-iIDFUmnprRv0xT] [-e excludedir] -f filename\n",
+			"usage:  %s [-ciIDFUmnprRv0xT] [-e excludedir] pathname...\n"
+			"usage:  %s [-ciIDFUmnprRv0xT] [-e excludedir] -f filename\n",
 			name, name);
 	} else {
 		fprintf(stderr,
@@ -146,7 +146,7 @@ int main(int argc, char **argv)
 	size_t buf_len, nthreads = 1;
 	const char *base;
 	int errors = 0;
-	const char *ropts = "e:f:hiIDlmno:pqrsvFURW0xT:";
+	const char *ropts = "ce:f:hiIDlmno:pqrsvFURW0xT:";
 	const char *sopts = "c:de:f:hiIDlmno:pqr:svACEFUR:W0T:";
 	const char *opts;
 	union selinux_callback cb;
@@ -223,7 +223,10 @@ int main(int argc, char **argv)
 	while ((opt = getopt(argc, argv, opts)) > 0) {
 		switch (opt) {
 		case 'c':
-			{
+			if (iamrestorecon) {
+				r_opts.count_relabeled = 1;
+				break;
+			} else {
 				FILE *policystream;
 
 				policyfile = optarg;
@@ -479,5 +482,18 @@ int main(int argc, char **argv)
 	if (r_opts.progress)
 		fprintf(stdout, "\n");
 
+	/* Output relabeled file count if requested */
+	if (r_opts.count_relabeled) {
+		long unsigned relabeled_count = selinux_restorecon_get_relabeled_files();
+		printf("Relabeled %lu files\n", relabeled_count);
+
+		/* Set exit code to number of relabeled files (capped at 254) */
+		if (errors) {
+			exit(-1);
+		} else {
+			exit(relabeled_count > 254 ? 254 : (int)relabeled_count);
+		}
+	}
+
 	exit(errors ? -1 : skipped_errors ? 1 : 0);
 }
-- 
2.51.0


             reply	other threads:[~2025-11-10 18:09 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-10 18:05 Vit Mojzis [this message]
2025-11-10 20:21 ` [PATCH] restorecon: Add option to count number of relabeled files Stephen Smalley
2025-11-10 20:23 ` William Roberts
2025-11-10 20:37   ` William Roberts
2025-11-11 14:44     ` Daniel Burgener
     [not found]       ` <CAFftDdoTR5ae1qORSjPuOj5ea1O15qtgrRiadhTp2HMh926swg@mail.gmail.com>
2025-11-13  0:43         ` William Roberts
2025-11-13 17:11           ` [PATCH v2] restorecon: Add option to count " Vit Mojzis
2025-11-13 20:17             ` William Roberts
2025-11-18  8:59             ` Petr Lautrbach
2025-11-18 20:11               ` [PATCH v3] " Vit Mojzis
2025-11-13 17:35           ` [PATCH] restorecon: Add option to count number of " Daniel Burgener
2025-11-13 19:29             ` William Roberts
2025-11-13 19:32               ` Daniel Burgener
2025-11-13 19:39                 ` William Roberts

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251110180935.202229-1-vmojzis@redhat.com \
    --to=vmojzis@redhat.com \
    --cc=selinux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).