diff --git a/.gitignore b/.gitignore
index f8649bc..609ac99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/libselinux-3.3.tar.gz
+SOURCES/libselinux-3.4.tar.gz
diff --git a/.libselinux.metadata b/.libselinux.metadata
index db6fb6a..5d9e33b 100644
--- a/.libselinux.metadata
+++ b/.libselinux.metadata
@@ -1 +1 @@
-70128f2395fc86b09c57db979972b4823b35e614 SOURCES/libselinux-3.3.tar.gz
+1fff75ad31eca7979740af01279d868ca8cbd249 SOURCES/libselinux-3.4.tar.gz
diff --git a/SOURCES/0001-Use-SHA-2-instead-of-SHA-1.patch b/SOURCES/0001-Use-SHA-2-instead-of-SHA-1.patch
index ed63a8c..fcd15a6 100644
--- a/SOURCES/0001-Use-SHA-2-instead-of-SHA-1.patch
+++ b/SOURCES/0001-Use-SHA-2-instead-of-SHA-1.patch
@@ -1,7 +1,8 @@
-From ec1b147076345478636de763ce5d4e8daa69afd6 Mon Sep 17 00:00:00 2001
+From 04f73fee2892753b3e81923d2ac3d338acfdbc4c Mon Sep 17 00:00:00 2001
 From: Petr Lautrbach <plautrba@redhat.com>
 Date: Fri, 30 Jul 2021 14:14:37 +0200
 Subject: [PATCH] Use SHA-2 instead of SHA-1
+Content-type: text/plain
 
 The use of SHA-1 in RHEL9 is deprecated
 ---
@@ -9,12 +10,12 @@ The use of SHA-1 in RHEL9 is deprecated
  libselinux/include/selinux/restorecon.h       |   4 +-
  libselinux/man/man3/selabel_digest.3          |   4 +-
  libselinux/man/man3/selabel_open.3            |   2 +-
- libselinux/man/man3/selinux_restorecon.3      |  16 +-
+ libselinux/man/man3/selinux_restorecon.3      |  18 +-
  .../man/man3/selinux_restorecon_xattr.3       |   2 +-
  libselinux/src/Makefile                       |   2 +-
  libselinux/src/label_file.c                   |  40 +--
  libselinux/src/label_internal.h               |  10 +-
- libselinux/src/label_support.c                |   8 +-
+ libselinux/src/label_support.c                |  10 +-
  libselinux/src/selinux_restorecon.c           |  24 +-
  libselinux/src/sha1.c                         | 220 -------------
  libselinux/src/sha1.h                         |  85 -----
@@ -22,7 +23,7 @@ The use of SHA-1 in RHEL9 is deprecated
  libselinux/src/sha256.h                       |  89 ++++++
  libselinux/utils/selabel_digest.c             |  26 +-
  .../selabel_get_digests_all_partial_matches.c |  28 +-
- 17 files changed, 469 insertions(+), 391 deletions(-)
+ 17 files changed, 471 insertions(+), 393 deletions(-)
  delete mode 100644 libselinux/src/sha1.c
  delete mode 100644 libselinux/src/sha1.h
  create mode 100644 libselinux/src/sha256.c
@@ -50,10 +51,10 @@ index e8983606d93b..a35d84d63b0a 100644
   * @num_specfiles: number of specfiles in the list.
   *
 diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
-index 466de39aac72..ca8ce768587a 100644
+index b10fe684eff9..8df4744505b3 100644
 --- a/libselinux/include/selinux/restorecon.h
 +++ b/libselinux/include/selinux/restorecon.h
-@@ -27,8 +27,8 @@ extern int selinux_restorecon(const char *pathname,
+@@ -41,8 +41,8 @@ extern int selinux_restorecon_parallel(const char *pathname,
   * restorecon_flags options
   */
  /*
@@ -83,7 +84,7 @@ index 56a008f00df0..5f7c42533d0e 100644
  with the number of entries in
  .IR num_specfiles .
 diff --git a/libselinux/man/man3/selabel_open.3 b/libselinux/man/man3/selabel_open.3
-index 971ebc1acd41..2cf2eb8a1410 100644
+index 0e03e1be111e..14ab888d2e03 100644
 --- a/libselinux/man/man3/selabel_open.3
 +++ b/libselinux/man/man3/selabel_open.3
 @@ -69,7 +69,7 @@ is used; a custom validation function can be provided via
@@ -96,10 +97,10 @@ index 971ebc1acd41..2cf2eb8a1410 100644
  .BR selabel_digest (3)
  .
 diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3
-index ad637406a30d..c4576fe79ff6 100644
+index 218aaf6d2ae5..5f6d4b386429 100644
 --- a/libselinux/man/man3/selinux_restorecon.3
 +++ b/libselinux/man/man3/selinux_restorecon.3
-@@ -28,7 +28,7 @@ If this is a directory and the
+@@ -36,7 +36,7 @@ If this is a directory and the
  .B SELINUX_RESTORECON_RECURSE
  has been set (for descending through directories), then
  .BR selinux_restorecon ()
@@ -108,7 +109,7 @@ index ad637406a30d..c4576fe79ff6 100644
  .BR selabel_get_digests_all_partial_matches (3)
  to an extended attribute of
  .IR security.sehash
-@@ -47,7 +47,7 @@ will take place.
+@@ -55,7 +55,7 @@ will take place.
  .br
  The
  .IR restorecon_flags
@@ -117,7 +118,7 @@ index ad637406a30d..c4576fe79ff6 100644
  .RS
  .B SELINUX_RESTORECON_SKIP_DIGEST
  .br
-@@ -65,8 +65,8 @@ Do not check or update any extended attribute
+@@ -73,8 +73,8 @@ Do not check or update any extended attribute
  entries.
  .sp
  .B SELINUX_RESTORECON_IGNORE_DIGEST
@@ -128,7 +129,7 @@ index ad637406a30d..c4576fe79ff6 100644
  .IR security.sehash
  extended attribute once relabeling has been completed successfully provided the
  .B SELINUX_RESTORECON_NOCHANGE
-@@ -84,7 +84,7 @@ default specfile context.
+@@ -95,7 +95,7 @@ default specfile context.
  .sp
  .B SELINUX_RESTORECON_RECURSE
  change file and directory labels recursively (descend directories)
@@ -137,8 +138,14 @@ index ad637406a30d..c4576fe79ff6 100644
  extended attribute as described in the
  .B NOTES
  section.
-@@ -158,7 +158,7 @@ to treat conflicting specifications, such as where two hardlinks for the
- same inode have different contexts, as errors.
+@@ -179,12 +179,12 @@ for fetching the ignored (skipped) error count after
+ 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
++walk, the specfile entries SHA256 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
@@ -146,7 +153,7 @@ index ad637406a30d..c4576fe79ff6 100644
  above is the default behavior. It is possible to change this by first calling
  .BR selabel_open (3)
  and not enabling the
-@@ -200,7 +200,7 @@ To improve performance when relabeling file systems recursively (e.g. the
+@@ -247,7 +247,7 @@ To improve performance when relabeling file systems recursively (e.g. the
  .B SELINUX_RESTORECON_RECURSE
  flag is set)
  .BR selinux_restorecon ()
@@ -155,7 +162,7 @@ index ad637406a30d..c4576fe79ff6 100644
  .BR selabel_get_digests_all_partial_matches (3)
  to an extended attribute named
  .IR security.sehash
-@@ -222,7 +222,7 @@ Should any of the specfile entries have changed, then when
+@@ -269,7 +269,7 @@ Should any of the specfile entries have changed, then when
  .BR selinux_restorecon ()
  is run again with the
  .B SELINUX_RESTORECON_RECURSE
@@ -178,10 +185,10 @@ index c56326814b94..098c840fc59b 100644
  .BR selabel_open (3)
  must be called specifying the required
 diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
-index 52c40f018f51..674a5ed3a6f8 100644
+index 04bf4f240168..222c3fa2d7c3 100644
 --- a/libselinux/src/Makefile
 +++ b/libselinux/src/Makefile
-@@ -120,7 +120,7 @@ DISABLE_FLAGS+= -DNO_MEDIA_BACKEND -DNO_DB_BACKEND -DNO_X_BACKEND \
+@@ -119,7 +119,7 @@ DISABLE_FLAGS+= -DNO_MEDIA_BACKEND -DNO_DB_BACKEND -DNO_X_BACKEND \
  	-DBUILD_HOST
  SRCS= callbacks.c freecon.c label.c label_file.c \
  	label_backends_android.c regex.c label_support.c \
@@ -191,10 +198,10 @@ index 52c40f018f51..674a5ed3a6f8 100644
  LABEL_BACKEND_ANDROID=y
  endif
 diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
-index 2e28d0474d73..c1306c9979e7 100644
+index 74ae9b9feb70..33d395e414f0 100644
 --- a/libselinux/src/label_file.c
 +++ b/libselinux/src/label_file.c
-@@ -1005,7 +1005,7 @@ static struct spec *lookup_common(struct selabel_handle *rec,
+@@ -1010,7 +1010,7 @@ static struct spec *lookup_common(struct selabel_handle *rec,
  
  /*
   * Returns true if the digest of all partial matched contexts is the same as
@@ -203,7 +210,7 @@ index 2e28d0474d73..c1306c9979e7 100644
   * digest will always be returned. The caller must free any returned digests.
   */
  static bool get_digests_all_partial_matches(struct selabel_handle *rec,
-@@ -1014,39 +1014,39 @@ static bool get_digests_all_partial_matches(struct selabel_handle *rec,
+@@ -1019,39 +1019,39 @@ static bool get_digests_all_partial_matches(struct selabel_handle *rec,
  					    uint8_t **xattr_digest,
  					    size_t *digest_len)
  {
@@ -254,7 +261,7 @@ index 2e28d0474d73..c1306c9979e7 100644
  		return true;
  
  	return false;
-@@ -1066,22 +1066,22 @@ static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key
+@@ -1071,22 +1071,22 @@ static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key
  		return false;
  	}
  
@@ -327,31 +334,44 @@ index 782c6aa8cc0c..304e8d96490a 100644
  };
  
 diff --git a/libselinux/src/label_support.c b/libselinux/src/label_support.c
-index 94ed6e4273cb..f53d73b609ab 100644
+index 54fd49a5b7b9..4003eb8dc7af 100644
 --- a/libselinux/src/label_support.c
 +++ b/libselinux/src/label_support.c
-@@ -115,15 +115,15 @@ int  read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
+@@ -115,7 +115,7 @@ int  read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
  /* Once all the specfiles are in the hash_buf, generate the hash. */
  void  digest_gen_hash(struct selabel_digest *digest)
  {
 -	Sha1Context context;
 +	Sha256Context context;
+ 	size_t remaining_size;
+ 	const unsigned char *ptr;
  
- 	/* If SELABEL_OPT_DIGEST not set then just return */
+@@ -123,19 +123,19 @@ void  digest_gen_hash(struct selabel_digest *digest)
  	if (!digest)
  		return;
  
 -	Sha1Initialise(&context);
--	Sha1Update(&context, digest->hashbuf, digest->hashbuf_size);
--	Sha1Finalise(&context, (SHA1_HASH *)digest->digest);
 +	Sha256Initialise(&context);
-+	Sha256Update(&context, digest->hashbuf, digest->hashbuf_size);
+ 
+ 	/* Process in blocks of UINT32_MAX bytes */
+ 	remaining_size = digest->hashbuf_size;
+ 	ptr = digest->hashbuf;
+ 	while (remaining_size > UINT32_MAX) {
+-		Sha1Update(&context, ptr, UINT32_MAX);
++		Sha256Update(&context, ptr, UINT32_MAX);
+ 		remaining_size -= UINT32_MAX;
+ 		ptr += UINT32_MAX;
+ 	}
+-	Sha1Update(&context, ptr, remaining_size);
++	Sha256Update(&context, ptr, remaining_size);
+ 
+-	Sha1Finalise(&context, (SHA1_HASH *)digest->digest);
 +	Sha256Finalise(&context, (SHA256_HASH *)digest->digest);
  	free(digest->hashbuf);
  	digest->hashbuf = NULL;
  	return;
 diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
-index 04d956504952..100c77108a27 100644
+index 9dd6be817832..dc222b425c95 100644
 --- a/libselinux/src/selinux_restorecon.c
 +++ b/libselinux/src/selinux_restorecon.c
 @@ -37,7 +37,7 @@
@@ -363,16 +383,16 @@ index 04d956504952..100c77108a27 100644
  
  #define STAR_COUNT 1024
  
-@@ -293,7 +293,7 @@ static int exclude_non_seclabel_mounts(void)
+@@ -305,7 +305,7 @@ static uint64_t exclude_non_seclabel_mounts(void)
  static int add_xattr_entry(const char *directory, bool delete_nonmatch,
  			   bool delete_all)
  {
 -	char *sha1_buf = NULL;
 +	char *sha256_buf = NULL;
  	size_t i, digest_len = 0;
- 	int rc, digest_result;
- 	bool match;
-@@ -316,15 +316,15 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
+ 	int rc;
+ 	enum digest_result digest_result;
+@@ -329,15 +329,15 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
  	}
  
  	/* Convert entry to a hex encoded string. */
@@ -391,7 +411,7 @@ index 04d956504952..100c77108a27 100644
  
  	digest_result = match ? MATCH : NOMATCH;
  
-@@ -344,7 +344,7 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
+@@ -357,7 +357,7 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
  	/* Now add entries to link list. */
  	new_entry = malloc(sizeof(struct dir_xattr));
  	if (!new_entry) {
@@ -400,7 +420,7 @@ index 04d956504952..100c77108a27 100644
  		goto oom;
  	}
  	new_entry->next = NULL;
-@@ -352,15 +352,15 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
+@@ -365,15 +365,15 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
  	new_entry->directory = strdup(directory);
  	if (!new_entry->directory) {
  		free(new_entry);
@@ -419,7 +439,7 @@ index 04d956504952..100c77108a27 100644
  		goto oom;
  	}
  
-@@ -374,7 +374,7 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
+@@ -387,7 +387,7 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
  		dir_xattr_last = new_entry;
  	}
  
@@ -428,7 +448,7 @@ index 04d956504952..100c77108a27 100644
  	return 0;
  
  oom:
-@@ -741,7 +741,7 @@ err:
+@@ -781,7 +781,7 @@ err:
  
  struct dir_hash_node {
  	char *path;
@@ -437,7 +457,7 @@ index 04d956504952..100c77108a27 100644
  	struct dir_hash_node *next;
  };
  /*
-@@ -1091,7 +1091,7 @@ int selinux_restorecon(const char *pathname_orig,
+@@ -1270,7 +1270,7 @@ static int selinux_restorecon_common(const char *pathname_orig,
  			if (setxattr(current->path,
  			    RESTORECON_PARTIAL_MATCH_DIGEST,
  			    current->digest,
@@ -1159,7 +1179,7 @@ index 000000000000..406ed869cd82
 +        SHA256_HASH*        Digest          // [in]
 +    );
 diff --git a/libselinux/utils/selabel_digest.c b/libselinux/utils/selabel_digest.c
-index 49408a0ba8d8..67befadd23c5 100644
+index 6a8313a2c88d..a69331f1c6b5 100644
 --- a/libselinux/utils/selabel_digest.c
 +++ b/libselinux/utils/selabel_digest.c
 @@ -15,8 +15,8 @@ static __attribute__ ((__noreturn__)) void usage(const char *progname)
@@ -1240,7 +1260,7 @@ index 49408a0ba8d8..67befadd23c5 100644
  	selabel_close(hnd);
  	return rc;
 diff --git a/libselinux/utils/selabel_get_digests_all_partial_matches.c b/libselinux/utils/selabel_get_digests_all_partial_matches.c
-index e28833d2ce97..900f018c0091 100644
+index c4e0f836b260..80723f714264 100644
 --- a/libselinux/utils/selabel_get_digests_all_partial_matches.c
 +++ b/libselinux/utils/selabel_get_digests_all_partial_matches.c
 @@ -18,8 +18,8 @@ static __attribute__ ((__noreturn__)) void usage(const char *progname)
@@ -1263,7 +1283,7 @@ index e28833d2ce97..900f018c0091 100644
  
  	struct selabel_handle *hnd;
  	struct selinux_opt selabel_option[] = {
-@@ -105,27 +105,27 @@ int main(int argc, char **argv)
+@@ -106,27 +106,27 @@ int main(int argc, char **argv)
  							 &xattr_digest,
  							 &digest_len);
  
@@ -1297,7 +1317,7 @@ index e28833d2ce97..900f018c0091 100644
  					       ftsent->fts_path);
  					printf("as file_context entry is \"<<none>>\"\n");
  					goto cleanup;
-@@ -135,25 +135,25 @@ int main(int argc, char **argv)
+@@ -136,25 +136,25 @@ int main(int argc, char **argv)
  				       ftsent->fts_path);
  
  				for (i = 0; i < digest_len; i++)
@@ -1329,5 +1349,5 @@ index e28833d2ce97..900f018c0091 100644
  		}
  		default:
 -- 
-2.32.0
+2.36.1
 
diff --git a/SOURCES/0002-Revert-libselinux-restorecon-pin-file-to-avoid-TOCTO.patch b/SOURCES/0002-Revert-libselinux-restorecon-pin-file-to-avoid-TOCTO.patch
new file mode 100644
index 0000000..f586640
--- /dev/null
+++ b/SOURCES/0002-Revert-libselinux-restorecon-pin-file-to-avoid-TOCTO.patch
@@ -0,0 +1,172 @@
+From f785c53174fd0ebad913e105382360f9d46205d8 Mon Sep 17 00:00:00 2001
+From: Petr Lautrbach <plautrba@redhat.com>
+Date: Tue, 31 May 2022 13:37:12 +0200
+Subject: [PATCH] Revert "libselinux: restorecon: pin file to avoid TOCTOU
+ issues"
+Content-type: text/plain
+
+This reverts commit 7e979b56fd2cee28f647376a7233d2ac2d12ca50.
+
+The reverted commit broke `setfiles` when it's run from a chroot
+without /proc mounted, e.g.
+
+    # chroot /mnt/sysimage
+
+    chroot# setfiles -e /proc -e /sys /sys /etc/selinux/targeted/contexts/files/file_contexts /
+    [strace]
+    openat(AT_FDCWD, "/", O_RDONLY|O_EXCL|O_NOFOLLOW|O_PATH) = 3
+    newfstatat(3, "", {st_mode=S_IFDIR|0555, st_size=4096, ...}, AT_EMPTY_PATH) = 0
+    mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1697c91000
+    fgetxattr(3, "security.selinux", 0x55be8881d3f0, 255) = -1 EBADF (Bad file descriptor)
+    fcntl(3, F_GETFL)                       = 0x220000 (flags O_RDONLY|O_NOFOLLOW|O_PATH)
+    getxattr("/proc/self/fd/3", "security.selinux", 0x55be8881d3f0, 255) = -1 ENOENT (No such file or directory)
+    [/strace]
+    setfiles: Could not set context for /:  No such file or directory
+
+Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
+---
+ libselinux/src/selinux_restorecon.c | 43 ++++++++++++-----------------
+ 1 file changed, 18 insertions(+), 25 deletions(-)
+
+diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
+index dc222b425c95..a50005353265 100644
+--- a/libselinux/src/selinux_restorecon.c
++++ b/libselinux/src/selinux_restorecon.c
+@@ -623,13 +623,13 @@ out:
+ 	return rc;
+ }
+ 
+-static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool first)
++static int restorecon_sb(const char *pathname, const struct stat *sb,
++			    struct rest_flags *flags, bool first)
+ {
+ 	char *newcon = NULL;
+ 	char *curcon = NULL;
+ 	char *newtypecon = NULL;
+-	int fd = -1, rc;
+-	struct stat stat_buf;
++	int rc;
+ 	bool updated = false;
+ 	const char *lookup_path = pathname;
+ 	float pc;
+@@ -644,21 +644,13 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ 		lookup_path += rootpathlen;
+ 	}
+ 
+-	fd = open(pathname, O_PATH | O_NOFOLLOW | O_EXCL);
+-	if (fd < 0)
+-		goto err;
+-
+-	rc = fstat(fd, &stat_buf);
+-	if (rc < 0)
+-		goto err;
+-
+ 	if (rootpath != NULL && lookup_path[0] == '\0')
+ 		/* this is actually the root dir of the alt root. */
+ 		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
+-						    stat_buf.st_mode);
++						    sb->st_mode);
+ 	else
+ 		rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path,
+-						    stat_buf.st_mode);
++						    sb->st_mode);
+ 
+ 	if (rc < 0) {
+ 		if (errno == ENOENT) {
+@@ -667,10 +659,10 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ 					    "Warning no default label for %s\n",
+ 					    lookup_path);
+ 
+-			goto out; /* no match, but not an error */
++			return 0; /* no match, but not an error */
+ 		}
+ 
+-		goto err;
++		return -1;
+ 	}
+ 
+ 	if (flags->progress) {
+@@ -690,17 +682,19 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ 	}
+ 
+ 	if (flags->add_assoc) {
+-		rc = filespec_add(stat_buf.st_ino, newcon, pathname, flags);
++		rc = filespec_add(sb->st_ino, newcon, pathname, flags);
+ 
+ 		if (rc < 0) {
+ 			selinux_log(SELINUX_ERROR,
+ 				    "filespec_add error: %s\n", pathname);
+-			goto out1;
++			freecon(newcon);
++			return -1;
+ 		}
+ 
+ 		if (rc > 0) {
+ 			/* Already an association and it took precedence. */
+-			goto out;
++			freecon(newcon);
++			return 0;
+ 		}
+ 	}
+ 
+@@ -708,7 +702,7 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ 		selinux_log(SELINUX_INFO, "%s matched by %s\n",
+ 			    pathname, newcon);
+ 
+-	if (fgetfilecon_raw(fd, &curcon) < 0) {
++	if (lgetfilecon_raw(pathname, &curcon) < 0) {
+ 		if (errno != ENODATA)
+ 			goto err;
+ 
+@@ -741,7 +735,7 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ 		}
+ 
+ 		if (!flags->nochange) {
+-			if (fsetfilecon(fd, newcon) < 0)
++			if (lsetfilecon(pathname, newcon) < 0)
+ 				goto err;
+ 			updated = true;
+ 		}
+@@ -766,8 +760,6 @@ static int restorecon_sb(const char *pathname, struct rest_flags *flags, bool fi
+ out:
+ 	rc = 0;
+ out1:
+-	if (fd >= 0)
+-		close(fd);
+ 	freecon(curcon);
+ 	freecon(newcon);
+ 	return rc;
+@@ -865,6 +857,7 @@ static void *selinux_restorecon_thread(void *arg)
+ 	FTSENT *ftsent;
+ 	int error;
+ 	char ent_path[PATH_MAX];
++	struct stat ent_st;
+ 	bool first = false;
+ 
+ 	if (state->parallel)
+@@ -962,11 +955,11 @@ loop_body:
+ 			/* fall through */
+ 		default:
+ 			strcpy(ent_path, ftsent->fts_path);
+-
++			ent_st = *ftsent->fts_statp;
+ 			if (state->parallel)
+ 				pthread_mutex_unlock(&state->mutex);
+ 
+-			error = restorecon_sb(ent_path, &state->flags,
++			error = restorecon_sb(ent_path, &ent_st, &state->flags,
+ 					      first);
+ 
+ 			if (state->parallel) {
+@@ -1162,7 +1155,7 @@ static int selinux_restorecon_common(const char *pathname_orig,
+ 			goto cleanup;
+ 		}
+ 
+-		error = restorecon_sb(pathname, &state.flags, true);
++		error = restorecon_sb(pathname, &sb, &state.flags, true);
+ 		goto cleanup;
+ 	}
+ 
+-- 
+2.36.1
+
diff --git a/SOURCES/0002-label_file-fix-a-data-race.patch b/SOURCES/0002-label_file-fix-a-data-race.patch
deleted file mode 100644
index 0554c37..0000000
--- a/SOURCES/0002-label_file-fix-a-data-race.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From 5844f389429f26a0a62a65561fa3006feaaf6f3b Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:32 +0200
-Subject: [PATCH] label_file: fix a data race
-
-The 'matches' member of 'struct spec' may be written to by different
-threads, so it needs to be accessed using the proper atomic constructs.
-Since the actual count of matches doesn't matter and is not used,
-convert this field to a bool and just atomically set/read it using GCC
-__atomic builtins (which are already being used in another place).
-
-If the compiler lacks support for __atomic builtins (which seem to have
-been introduced in GCC 4.1), just fail the compilation. I don't think
-it's worth tryin to invent a workaround to support a 15 years old
-compiler.
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/src/label_file.c | 15 +++++++++++++--
- libselinux/src/label_file.h |  2 +-
- 2 files changed, 14 insertions(+), 3 deletions(-)
-
-diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
-index c1306c9979e7..33d395e414f0 100644
---- a/libselinux/src/label_file.c
-+++ b/libselinux/src/label_file.c
-@@ -951,7 +951,12 @@ static struct spec **lookup_all(struct selabel_handle *rec,
- 			rc = regex_match(spec->regex, key, partial);
- 			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
- 				if (rc == REGEX_MATCH) {
--					spec->matches++;
-+#ifdef __ATOMIC_RELAXED
-+					__atomic_store_n(&spec->any_matches,
-+							 true, __ATOMIC_RELAXED);
-+#else
-+#error "Please use a compiler that supports __atomic builtins"
-+#endif
- 				}
- 
- 				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
-@@ -1249,9 +1254,15 @@ static void stats(struct selabel_handle *rec)
- 	struct saved_data *data = (struct saved_data *)rec->data;
- 	unsigned int i, nspec = data->nspec;
- 	struct spec *spec_arr = data->spec_arr;
-+	bool any_matches;
- 
- 	for (i = 0; i < nspec; i++) {
--		if (spec_arr[i].matches == 0) {
-+#ifdef __ATOMIC_RELAXED
-+		any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED);
-+#else
-+#error "Please use a compiler that supports __atomic builtins"
-+#endif
-+		if (!any_matches) {
- 			if (spec_arr[i].type_str) {
- 				COMPAT_LOG(SELINUX_WARNING,
- 				    "Warning!  No matches for (%s, %s, %s)\n",
-diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
-index 343ffc705e43..b453e13f8075 100644
---- a/libselinux/src/label_file.h
-+++ b/libselinux/src/label_file.h
-@@ -51,7 +51,7 @@ struct spec {
- 	bool regex_compiled; /* bool to indicate if the regex is compiled */
- 	pthread_mutex_t regex_lock; /* lock for lazy compilation of regex */
- 	mode_t mode;		/* mode format value */
--	int matches;		/* number of matching pathnames */
-+	bool any_matches;	/* did any pathname match? */
- 	int stem_id;		/* indicates which stem-compression item */
- 	char hasMetaChars;	/* regular expression has meta-chars */
- 	char from_mmap;		/* this spec is from an mmap of the data */
--- 
-2.33.1
-
diff --git a/SOURCES/0003-selinux_restorecon-simplify-fl_head-allocation-by-us.patch b/SOURCES/0003-selinux_restorecon-simplify-fl_head-allocation-by-us.patch
deleted file mode 100644
index 01c6d25..0000000
--- a/SOURCES/0003-selinux_restorecon-simplify-fl_head-allocation-by-us.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 5dd3a11842c08a25a0f7ab798ce85710fe1e8f1f Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:33 +0200
-Subject: [PATCH] selinux_restorecon: simplify fl_head allocation by using
- calloc()
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/src/selinux_restorecon.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
-index 100c77108a27..e29a2c390182 100644
---- a/libselinux/src/selinux_restorecon.c
-+++ b/libselinux/src/selinux_restorecon.c
-@@ -425,10 +425,9 @@ static int filespec_add(ino_t ino, const char *con, const char *file,
- 	struct stat64 sb;
- 
- 	if (!fl_head) {
--		fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
-+		fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t));
- 		if (!fl_head)
- 			goto oom;
--		memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
- 	}
- 
- 	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
--- 
-2.33.1
-
diff --git a/SOURCES/0004-selinux_restorecon-protect-file_spec-list-with-a-mut.patch b/SOURCES/0004-selinux_restorecon-protect-file_spec-list-with-a-mut.patch
deleted file mode 100644
index 1143f33..0000000
--- a/SOURCES/0004-selinux_restorecon-protect-file_spec-list-with-a-mut.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 4598a46c5ed12248a3a6e1dbe1b5a3dca52bacac Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:34 +0200
-Subject: [PATCH] selinux_restorecon: protect file_spec list with a mutex
-
-Not very useful on its own, but will allow to implement a parallel
-version of selinux_restorecon() in subsequent patches.
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/src/selinux_restorecon.c | 16 ++++++++++++++--
- 1 file changed, 14 insertions(+), 2 deletions(-)
-
-diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
-index e29a2c390182..43acbace309d 100644
---- a/libselinux/src/selinux_restorecon.c
-+++ b/libselinux/src/selinux_restorecon.c
-@@ -411,6 +411,7 @@ typedef struct file_spec {
- } file_spec_t;
- 
- static file_spec_t *fl_head;
-+static pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER;
- 
- /*
-  * Try to add an association between an inode and a context. If there is a
-@@ -424,6 +425,8 @@ static int filespec_add(ino_t ino, const char *con, const char *file,
- 	int h, ret;
- 	struct stat64 sb;
- 
-+	__pthread_mutex_lock(&fl_mutex);
-+
- 	if (!fl_head) {
- 		fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t));
- 		if (!fl_head)
-@@ -444,11 +447,11 @@ static int filespec_add(ino_t ino, const char *con, const char *file,
- 				fl->con = strdup(con);
- 				if (!fl->con)
- 					goto oom;
--				return 1;
-+				goto unlock_1;
- 			}
- 
- 			if (strcmp(fl->con, con) == 0)
--				return 1;
-+				goto unlock_1;
- 
- 			selinux_log(SELINUX_ERROR,
- 				"conflicting specifications for %s and %s, using %s.\n",
-@@ -457,6 +460,9 @@ static int filespec_add(ino_t ino, const char *con, const char *file,
- 			fl->file = strdup(file);
- 			if (!fl->file)
- 				goto oom;
-+
-+			__pthread_mutex_unlock(&fl_mutex);
-+
- 			if (flags->conflicterror) {
- 				selinux_log(SELINUX_ERROR,
- 				"treating conflicting specifications as an error.\n");
-@@ -481,13 +487,19 @@ static int filespec_add(ino_t ino, const char *con, const char *file,
- 		goto oom_freefl;
- 	fl->next = prevfl->next;
- 	prevfl->next = fl;
-+
-+	__pthread_mutex_unlock(&fl_mutex);
- 	return 0;
- 
- oom_freefl:
- 	free(fl);
- oom:
-+	__pthread_mutex_unlock(&fl_mutex);
- 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
- 	return -1;
-+unlock_1:
-+	__pthread_mutex_unlock(&fl_mutex);
-+	return 1;
- }
- 
- /*
--- 
-2.33.1
-
diff --git a/SOURCES/0005-libselinux-make-selinux_log-thread-safe.patch b/SOURCES/0005-libselinux-make-selinux_log-thread-safe.patch
deleted file mode 100644
index 078d5b4..0000000
--- a/SOURCES/0005-libselinux-make-selinux_log-thread-safe.patch
+++ /dev/null
@@ -1,88 +0,0 @@
-From c2e4cf5b21e8c775c669f3933d25a0946774ec0d Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:35 +0200
-Subject: [PATCH] libselinux: make selinux_log() thread-safe
-
-Ensure that selinux_log() is thread-safe by guarding the call to the
-underlying callback with a mutex.
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/src/callbacks.c |  8 +++++---
- libselinux/src/callbacks.h | 13 ++++++++++++-
- 2 files changed, 17 insertions(+), 4 deletions(-)
-
-diff --git a/libselinux/src/callbacks.c b/libselinux/src/callbacks.c
-index c18ccc54754a..469c4055f4d7 100644
---- a/libselinux/src/callbacks.c
-+++ b/libselinux/src/callbacks.c
-@@ -10,6 +10,8 @@
- #include <selinux/selinux.h>
- #include "callbacks.h"
- 
-+pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
-+
- /* default implementations */
- static int __attribute__ ((format(printf, 2, 3)))
- default_selinux_log(int type __attribute__((unused)), const char *fmt, ...)
-@@ -56,7 +58,7 @@ default_selinux_policyload(int seqno __attribute__((unused)))
- 
- /* callback pointers */
- int __attribute__ ((format(printf, 2, 3)))
--(*selinux_log)(int, const char *, ...) =
-+(*selinux_log_direct)(int, const char *, ...) =
- 	default_selinux_log;
- 
- int
-@@ -81,7 +83,7 @@ selinux_set_callback(int type, union selinux_callback cb)
- {
- 	switch (type) {
- 	case SELINUX_CB_LOG:
--		selinux_log = cb.func_log;
-+		selinux_log_direct = cb.func_log;
- 		break;
- 	case SELINUX_CB_AUDIT:
- 		selinux_audit = cb.func_audit;
-@@ -106,7 +108,7 @@ selinux_get_callback(int type)
- 
- 	switch (type) {
- 	case SELINUX_CB_LOG:
--		cb.func_log = selinux_log;
-+		cb.func_log = selinux_log_direct;
- 		break;
- 	case SELINUX_CB_AUDIT:
- 		cb.func_audit = selinux_audit;
-diff --git a/libselinux/src/callbacks.h b/libselinux/src/callbacks.h
-index 03d87f0cbdfe..f4dab15789f9 100644
---- a/libselinux/src/callbacks.h
-+++ b/libselinux/src/callbacks.h
-@@ -10,9 +10,11 @@
- #include <string.h>
- #include <selinux/selinux.h>
- 
-+#include "selinux_internal.h"
-+
- /* callback pointers */
- extern int __attribute__ ((format(printf, 2, 3)))
--(*selinux_log) (int type, const char *, ...) ;
-+(*selinux_log_direct) (int type, const char *, ...) ;
- 
- extern int
- (*selinux_audit) (void *, security_class_t, char *, size_t) ;
-@@ -26,4 +28,13 @@ extern int
- extern int
- (*selinux_netlink_policyload) (int seqno) ;
- 
-+/* Thread-safe selinux_log() function */
-+extern pthread_mutex_t log_mutex;
-+
-+#define selinux_log(type, ...) do { \
-+	__pthread_mutex_lock(&log_mutex); \
-+	selinux_log_direct(type, __VA_ARGS__); \
-+	__pthread_mutex_unlock(&log_mutex); \
-+} while(0)
-+
- #endif				/* _SELINUX_CALLBACKS_H_ */
--- 
-2.33.1
-
diff --git a/SOURCES/0006-libselinux-make-is_context_customizable-thread-safe.patch b/SOURCES/0006-libselinux-make-is_context_customizable-thread-safe.patch
deleted file mode 100644
index 3dc8213..0000000
--- a/SOURCES/0006-libselinux-make-is_context_customizable-thread-safe.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 9a8db9356c07d16a9337df416a3261c0527afeb7 Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:36 +0200
-Subject: [PATCH] libselinux: make is_context_customizable() thread-safe
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the __selinux_once() macro to ensure that threads don't race to
-initialize the list of customizable types.
-
-Reported-by: Christian Göttsche <cgzones@googlemail.com>
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
-Tested-by: Christian Göttsche <cgzones@googlemail.com>
----
- libselinux/src/is_customizable_type.c | 23 +++++++++++------------
- 1 file changed, 11 insertions(+), 12 deletions(-)
-
-diff --git a/libselinux/src/is_customizable_type.c b/libselinux/src/is_customizable_type.c
-index 1b17860c3622..f83e1e83e944 100644
---- a/libselinux/src/is_customizable_type.c
-+++ b/libselinux/src/is_customizable_type.c
-@@ -9,7 +9,10 @@
- #include "selinux_internal.h"
- #include "context_internal.h"
- 
--static int get_customizable_type_list(char *** retlist)
-+static char **customizable_list = NULL;
-+static pthread_once_t customizable_once = PTHREAD_ONCE_INIT;
-+
-+static void customizable_init(void)
- {
- 	FILE *fp;
- 	char *buf;
-@@ -18,12 +21,12 @@ static int get_customizable_type_list(char *** retlist)
- 
- 	fp = fopen(selinux_customizable_types_path(), "re");
- 	if (!fp)
--		return -1;
-+		return;
- 
- 	buf = malloc(selinux_page_size);
- 	if (!buf) {
- 		fclose(fp);
--		return -1;
-+		return;
- 	}
- 	while (fgets_unlocked(buf, selinux_page_size, fp) && ctr < UINT_MAX) {
- 		ctr++;
-@@ -54,23 +57,19 @@ static int get_customizable_type_list(char *** retlist)
- 	fclose(fp);
- 	free(buf);
- 	if (!list)
--		return -1;
--	*retlist = list;
--	return 0;
-+		return;
-+	customizable_list = list;
- }
- 
--static char **customizable_list = NULL;
--
- int is_context_customizable(const char * scontext)
- {
- 	int i;
- 	const char *type;
- 	context_t c;
- 
--	if (!customizable_list) {
--		if (get_customizable_type_list(&customizable_list) != 0)
--			return -1;
--	}
-+	__selinux_once(customizable_once, customizable_init);
-+	if (!customizable_list)
-+		return -1;
- 
- 	c = context_new(scontext);
- 	if (!c)
--- 
-2.33.1
-
diff --git a/SOURCES/0007-selinux_restorecon-add-a-global-mutex-to-synchronize.patch b/SOURCES/0007-selinux_restorecon-add-a-global-mutex-to-synchronize.patch
deleted file mode 100644
index a5affd5..0000000
--- a/SOURCES/0007-selinux_restorecon-add-a-global-mutex-to-synchronize.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 73310c9694724b3ef54bbf3a3193dbb0a68ecc3b Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:37 +0200
-Subject: [PATCH] selinux_restorecon: add a global mutex to synchronize
- progress output
-
-Another small incremental change to pave the way for a parallel
-selinux_restorecon() function.
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/src/selinux_restorecon.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
-index 43acbace309d..169dfe3ae232 100644
---- a/libselinux/src/selinux_restorecon.c
-+++ b/libselinux/src/selinux_restorecon.c
-@@ -60,6 +60,7 @@ static int exclude_count = 0;
- static struct edir *exclude_lst = NULL;
- static uint64_t fc_count = 0;	/* Number of files processed so far */
- static uint64_t efile_count;	/* Estimated total number of files */
-+static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
- 
- /* Store information on directories with xattr's. */
- static struct dir_xattr *dir_xattr_list;
-@@ -647,6 +648,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
- 	}
- 
- 	if (flags->progress) {
-+		__pthread_mutex_lock(&progress_mutex);
- 		fc_count++;
- 		if (fc_count % STAR_COUNT == 0) {
- 			if (flags->mass_relabel && efile_count > 0) {
-@@ -658,6 +660,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
- 			}
- 			fflush(stdout);
- 		}
-+		__pthread_mutex_unlock(&progress_mutex);
- 	}
- 
- 	if (flags->add_assoc) {
--- 
-2.33.1
-
diff --git a/SOURCES/0008-selinux_restorecon-introduce-selinux_restorecon_para.patch b/SOURCES/0008-selinux_restorecon-introduce-selinux_restorecon_para.patch
deleted file mode 100644
index a16023e..0000000
--- a/SOURCES/0008-selinux_restorecon-introduce-selinux_restorecon_para.patch
+++ /dev/null
@@ -1,800 +0,0 @@
-From 847282ce385a4fc03092eb10422b1878590e9bdd Mon Sep 17 00:00:00 2001
-From: Ondrej Mosnacek <omosnace@redhat.com>
-Date: Tue, 26 Oct 2021 13:52:38 +0200
-Subject: [PATCH] selinux_restorecon: introduce selinux_restorecon_parallel(3)
-
-Refactor selinux_restorecon(3) to allow for distributing the relabeling
-to multiple threads and add a new function
-selinux_restorecon_parallel(3), which allows specifying the number of
-threads to use. The existing selinux_restorecon(3) function maintains
-the same interface and maintains the same behavior (i.e. relabeling is
-done on a single thread).
-
-The parallel implementation takes a simple approach of performing all
-the directory tree traversal in a critical section and only letting the
-relabeling of individual objects run in parallel. Thankfully, this
-approach turns out to be efficient enough in practice, as shown by
-restorecon benchmarks (detailed in a subsequent patch that switches
-setfiles & restorecon to use selinux_restorecon_parallel(3)).
-
-Note that to be able to use the parallelism, the calling application/
-library must be explicitly linked to the libpthread library (statically
-or dynamically). This is necessary to mantain the requirement that
-libselinux shouldn't explicitly link with libpthread. (I don't know what
-exactly was the reason behind this requirement as the commit logs are
-fuzzy, but special care has been taken in the past to maintain it, so I
-didn't want to break it...)
-
-Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
----
- libselinux/include/selinux/restorecon.h       |  14 +
- libselinux/man/man3/selinux_restorecon.3      |  29 ++
- .../man/man3/selinux_restorecon_parallel.3    |   1 +
- libselinux/src/libselinux.map                 |   5 +
- libselinux/src/selinux_internal.h             |  16 +
- libselinux/src/selinux_restorecon.c           | 436 ++++++++++++------
- libselinux/src/selinuxswig_python.i           |   6 +-
- libselinux/src/selinuxswig_python_exception.i |   8 +
- 8 files changed, 368 insertions(+), 147 deletions(-)
- create mode 100644 libselinux/man/man3/selinux_restorecon_parallel.3
-
-diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
-index ca8ce768587a..8f9a030cda98 100644
---- a/libselinux/include/selinux/restorecon.h
-+++ b/libselinux/include/selinux/restorecon.h
-@@ -2,6 +2,7 @@
- #define _RESTORECON_H_
- 
- #include <sys/types.h>
-+#include <stddef.h>
- #include <stdarg.h>
- 
- #ifdef __cplusplus
-@@ -23,6 +24,19 @@ extern "C" {
-  */
- extern int selinux_restorecon(const char *pathname,
- 				    unsigned int restorecon_flags);
-+/**
-+ * selinux_restorecon_parallel - Relabel files, optionally use more threads.
-+ * @pathname: specifies file/directory to relabel.
-+ * @restorecon_flags: specifies the actions to be performed when relabeling.
-+ * @nthreads: specifies the number of threads to use (0 = use number of CPUs
-+ *            currently online)
-+ *
-+ * Same as selinux_restorecon(3), but allows to use multiple threads to do
-+ * the work.
-+ */
-+extern int selinux_restorecon_parallel(const char *pathname,
-+				       unsigned int restorecon_flags,
-+				       size_t nthreads);
- /*
-  * restorecon_flags options
-  */
-diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3
-index c4576fe79ff6..500845917fb8 100644
---- a/libselinux/man/man3/selinux_restorecon.3
-+++ b/libselinux/man/man3/selinux_restorecon.3
-@@ -11,6 +11,14 @@ selinux_restorecon \- restore file(s) default SELinux security contexts
- .br
- .BI "unsigned int " restorecon_flags ");"
- .in
-+.sp
-+.BI "int selinux_restorecon_parallel(const char *" pathname ,
-+.in +\w'int selinux_restorecon_parallel('u
-+.br
-+.BI "unsigned int " restorecon_flags ","
-+.br
-+.BI "size_t " nthreads ");"
-+.in
- .
- .SH "DESCRIPTION"
- .BR selinux_restorecon ()
-@@ -187,6 +195,27 @@ unless the
- .B SELINUX_RESTORECON_IGNORE_MOUNTS
- flag has been set.
- .RE
-+.sp
-+.BR selinux_restorecon_parallel()
-+is similar to
-+.BR selinux_restorecon (3),
-+but accepts another parameter that allows to run relabeling over multiple
-+threads:
-+.sp
-+.RS
-+.IR nthreads
-+specifies the number of threads to use during relabeling. When set to 1,
-+the behavior is the same as calling
-+.BR selinux_restorecon (3).
-+When set to 0, the function will try to use as many threads as there are
-+online CPU cores. When set to any other number, the function will try to use
-+the given number of threads.
-+.sp
-+Note that to use the parallel relabeling capability, the calling process
-+must be linked with the
-+.B libpthread
-+library (either at compile time or dynamically at run time). Otherwise the
-+function will print a warning and fall back to the single threaded mode.
- .
- .SH "RETURN VALUE"
- On success, zero is returned.  On error, \-1 is returned and
-diff --git a/libselinux/man/man3/selinux_restorecon_parallel.3 b/libselinux/man/man3/selinux_restorecon_parallel.3
-new file mode 100644
-index 000000000000..092d8412cc93
---- /dev/null
-+++ b/libselinux/man/man3/selinux_restorecon_parallel.3
-@@ -0,0 +1 @@
-+.so man3/selinux_restorecon.3
-diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map
-index 2a368e93f9fd..d138e951ef0d 100644
---- a/libselinux/src/libselinux.map
-+++ b/libselinux/src/libselinux.map
-@@ -240,3 +240,8 @@ LIBSELINUX_1.0 {
-   local:
-     *;
- };
-+
-+LIBSELINUX_3.3 {
-+  global:
-+    selinux_restorecon_parallel;
-+} LIBSELINUX_1.0;
-diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
-index 27e9ac532c3f..297dcf26dee3 100644
---- a/libselinux/src/selinux_internal.h
-+++ b/libselinux/src/selinux_internal.h
-@@ -69,6 +69,22 @@ extern int selinux_page_size ;
- 			pthread_mutex_unlock(LOCK);		\
- 	} while (0)
- 
-+#pragma weak pthread_create
-+#pragma weak pthread_join
-+#pragma weak pthread_cond_init
-+#pragma weak pthread_cond_signal
-+#pragma weak pthread_cond_destroy
-+#pragma weak pthread_cond_wait
-+
-+/* check if all functions needed to do parallel operations are available */
-+#define __pthread_supported (					\
-+	pthread_create &&					\
-+	pthread_join &&						\
-+	pthread_cond_init &&					\
-+	pthread_cond_destroy &&					\
-+	pthread_cond_signal &&					\
-+	pthread_cond_wait					\
-+)
- 
- #define SELINUXDIR "/etc/selinux/"
- #define SELINUXCONFIG SELINUXDIR "config"
-diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
-index 169dfe3ae232..f7e84657d09d 100644
---- a/libselinux/src/selinux_restorecon.c
-+++ b/libselinux/src/selinux_restorecon.c
-@@ -610,7 +610,7 @@ out:
- }
- 
- static int restorecon_sb(const char *pathname, const struct stat *sb,
--			    struct rest_flags *flags)
-+			    struct rest_flags *flags, bool first)
- {
- 	char *newcon = NULL;
- 	char *curcon = NULL;
-@@ -639,7 +639,7 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
- 						    sb->st_mode);
- 
- 	if (rc < 0) {
--		if (errno == ENOENT && flags->warnonnomatch)
-+		if (errno == ENOENT && flags->warnonnomatch && first)
- 			selinux_log(SELINUX_INFO,
- 				    "Warning no default label for %s\n",
- 				    lookup_path);
-@@ -814,66 +814,215 @@ oom:
- 	goto free;
- }
- 
-+struct rest_state {
-+	struct rest_flags flags;
-+	dev_t dev_num;
-+	struct statfs sfsb;
-+	bool ignore_digest;
-+	bool setrestorecondigest;
-+	bool parallel;
- 
--/*
-- * Public API
-- */
-+	FTS *fts;
-+	FTSENT *ftsent_first;
-+	struct dir_hash_node *head, *current;
-+	bool abort;
-+	int error;
-+	int saved_errno;
-+	pthread_mutex_t mutex;
-+};
- 
--/* selinux_restorecon(3) - Main function that is responsible for labeling */
--int selinux_restorecon(const char *pathname_orig,
--		       unsigned int restorecon_flags)
-+static void *selinux_restorecon_thread(void *arg)
- {
--	struct rest_flags flags;
-+	struct rest_state *state = arg;
-+	FTS *fts = state->fts;
-+	FTSENT *ftsent;
-+	int error;
-+	char ent_path[PATH_MAX];
-+	struct stat ent_st;
-+	bool first = false;
-+
-+	if (state->parallel)
-+		pthread_mutex_lock(&state->mutex);
-+
-+	if (state->ftsent_first) {
-+		ftsent = state->ftsent_first;
-+		state->ftsent_first = NULL;
-+		first = true;
-+		goto loop_body;
-+	}
-+
-+	while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) {
-+loop_body:
-+		/* If the FTS_XDEV flag is set and the device is different */
-+		if (state->flags.set_xdev &&
-+		    ftsent->fts_statp->st_dev != state->dev_num)
-+			continue;
-+
-+		switch (ftsent->fts_info) {
-+		case FTS_DC:
-+			selinux_log(SELINUX_ERROR,
-+				    "Directory cycle on %s.\n",
-+				    ftsent->fts_path);
-+			errno = ELOOP;
-+			state->error = -1;
-+			state->abort = true;
-+			goto finish;
-+		case FTS_DP:
-+			continue;
-+		case FTS_DNR:
-+			error = errno;
-+			errno = ftsent->fts_errno;
-+			selinux_log(SELINUX_ERROR,
-+				    "Could not read %s: %m.\n",
-+				    ftsent->fts_path);
-+			errno = error;
-+			fts_set(fts, ftsent, FTS_SKIP);
-+			continue;
-+		case FTS_NS:
-+			error = errno;
-+			errno = ftsent->fts_errno;
-+			selinux_log(SELINUX_ERROR,
-+				    "Could not stat %s: %m.\n",
-+				    ftsent->fts_path);
-+			errno = error;
-+			fts_set(fts, ftsent, FTS_SKIP);
-+			continue;
-+		case FTS_ERR:
-+			error = errno;
-+			errno = ftsent->fts_errno;
-+			selinux_log(SELINUX_ERROR,
-+				    "Error on %s: %m.\n",
-+				    ftsent->fts_path);
-+			errno = error;
-+			fts_set(fts, ftsent, FTS_SKIP);
-+			continue;
-+		case FTS_D:
-+			if (state->sfsb.f_type == SYSFS_MAGIC &&
-+			    !selabel_partial_match(fc_sehandle,
-+			    ftsent->fts_path)) {
-+				fts_set(fts, ftsent, FTS_SKIP);
-+				continue;
-+			}
-+
-+			if (check_excluded(ftsent->fts_path)) {
-+				fts_set(fts, ftsent, FTS_SKIP);
-+				continue;
-+			}
- 
--	flags.nochange = (restorecon_flags &
-+			if (state->setrestorecondigest) {
-+				struct dir_hash_node *new_node = NULL;
-+
-+				if (check_context_match_for_dir(ftsent->fts_path,
-+								&new_node,
-+								state->error) &&
-+								!state->ignore_digest) {
-+					selinux_log(SELINUX_INFO,
-+						"Skipping restorecon on directory(%s)\n",
-+						    ftsent->fts_path);
-+					fts_set(fts, ftsent, FTS_SKIP);
-+					continue;
-+				}
-+
-+				if (new_node && !state->error) {
-+					if (!state->current) {
-+						state->current = new_node;
-+						state->head = state->current;
-+					} else {
-+						state->current->next = new_node;
-+						state->current = new_node;
-+					}
-+				}
-+			}
-+			/* fall through */
-+		default:
-+			strcpy(ent_path, ftsent->fts_path);
-+			ent_st = *ftsent->fts_statp;
-+			if (state->parallel)
-+				pthread_mutex_unlock(&state->mutex);
-+
-+			error = restorecon_sb(ent_path, &ent_st, &state->flags,
-+					      first);
-+
-+			if (state->parallel) {
-+				pthread_mutex_lock(&state->mutex);
-+				if (state->abort)
-+					goto unlock;
-+			}
-+
-+			state->error |= error;
-+			first = false;
-+			if (error && state->flags.abort_on_error) {
-+				state->abort = true;
-+				goto finish;
-+			}
-+			break;
-+		}
-+	}
-+
-+finish:
-+	if (!state->saved_errno)
-+		state->saved_errno = errno;
-+unlock:
-+	if (state->parallel)
-+		pthread_mutex_unlock(&state->mutex);
-+	return NULL;
-+}
-+
-+static int selinux_restorecon_common(const char *pathname_orig,
-+				     unsigned int restorecon_flags,
-+				     size_t nthreads)
-+{
-+	struct rest_state state;
-+
-+	state.flags.nochange = (restorecon_flags &
- 		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
--	flags.verbose = (restorecon_flags &
-+	state.flags.verbose = (restorecon_flags &
- 		    SELINUX_RESTORECON_VERBOSE) ? true : false;
--	flags.progress = (restorecon_flags &
-+	state.flags.progress = (restorecon_flags &
- 		    SELINUX_RESTORECON_PROGRESS) ? true : false;
--	flags.mass_relabel = (restorecon_flags &
-+	state.flags.mass_relabel = (restorecon_flags &
- 		    SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
--	flags.recurse = (restorecon_flags &
-+	state.flags.recurse = (restorecon_flags &
- 		    SELINUX_RESTORECON_RECURSE) ? true : false;
--	flags.set_specctx = (restorecon_flags &
-+	state.flags.set_specctx = (restorecon_flags &
- 		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
--	flags.userealpath = (restorecon_flags &
-+	state.flags.userealpath = (restorecon_flags &
- 		   SELINUX_RESTORECON_REALPATH) ? true : false;
--	flags.set_xdev = (restorecon_flags &
-+	state.flags.set_xdev = (restorecon_flags &
- 		   SELINUX_RESTORECON_XDEV) ? true : false;
--	flags.add_assoc = (restorecon_flags &
-+	state.flags.add_assoc = (restorecon_flags &
- 		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
--	flags.abort_on_error = (restorecon_flags &
-+	state.flags.abort_on_error = (restorecon_flags &
- 		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
--	flags.syslog_changes = (restorecon_flags &
-+	state.flags.syslog_changes = (restorecon_flags &
- 		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
--	flags.log_matches = (restorecon_flags &
-+	state.flags.log_matches = (restorecon_flags &
- 		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
--	flags.ignore_noent = (restorecon_flags &
-+	state.flags.ignore_noent = (restorecon_flags &
- 		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
--	flags.warnonnomatch = true;
--	flags.conflicterror = (restorecon_flags &
-+	state.flags.warnonnomatch = true;
-+	state.flags.conflicterror = (restorecon_flags &
- 		   SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false;
- 	ignore_mounts = (restorecon_flags &
- 		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
--	bool ignore_digest = (restorecon_flags &
-+	state.ignore_digest = (restorecon_flags &
- 		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
--	bool setrestorecondigest = true;
-+	state.setrestorecondigest = true;
-+
-+	state.head = NULL;
-+	state.current = NULL;
-+	state.abort = false;
-+	state.error = 0;
-+	state.saved_errno = 0;
- 
- 	struct stat sb;
--	struct statfs sfsb;
--	FTS *fts;
--	FTSENT *ftsent;
- 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
- 	char *paths[2] = { NULL, NULL };
- 	int fts_flags, error, sverrno;
--	dev_t dev_num = 0;
- 	struct dir_hash_node *current = NULL;
--	struct dir_hash_node *head = NULL;
--	int errno_tmp;
- 
--	if (flags.verbose && flags.progress)
--		flags.verbose = false;
-+	if (state.flags.verbose && state.flags.progress)
-+		state.flags.verbose = false;
- 
- 	__selinux_once(fc_once, restorecon_init);
- 
-@@ -886,13 +1035,31 @@ int selinux_restorecon(const char *pathname_orig,
- 	 */
- 	if (selabel_no_digest ||
- 	    (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST))
--		setrestorecondigest = false;
-+		state.setrestorecondigest = false;
-+
-+	if (!__pthread_supported) {
-+		if (nthreads != 1) {
-+			nthreads = 1;
-+			selinux_log(SELINUX_WARNING,
-+				"Threading functionality not available, falling back to 1 thread.");
-+		}
-+	} else if (nthreads == 0) {
-+		long nproc = sysconf(_SC_NPROCESSORS_ONLN);
-+
-+		if (nproc > 0) {
-+			nthreads = nproc;
-+		} else {
-+			nthreads = 1;
-+			selinux_log(SELINUX_WARNING,
-+				"Unable to detect CPU count, falling back to 1 thread.");
-+		}
-+	}
- 
- 	/*
- 	 * Convert passed-in pathname to canonical pathname by resolving
- 	 * realpath of containing dir, then appending last component name.
- 	 */
--	if (flags.userealpath) {
-+	if (state.flags.userealpath) {
- 		char *basename_cpy = strdup(pathname_orig);
- 		if (!basename_cpy)
- 			goto realpatherr;
-@@ -937,7 +1104,7 @@ int selinux_restorecon(const char *pathname_orig,
- 	paths[0] = pathname;
- 
- 	if (lstat(pathname, &sb) < 0) {
--		if (flags.ignore_noent && errno == ENOENT) {
-+		if (state.flags.ignore_noent && errno == ENOENT) {
- 			free(pathdnamer);
- 			free(pathname);
- 			return 0;
-@@ -952,21 +1119,21 @@ int selinux_restorecon(const char *pathname_orig,
- 
- 	/* Skip digest if not a directory */
- 	if (!S_ISDIR(sb.st_mode))
--		setrestorecondigest = false;
-+		state.setrestorecondigest = false;
- 
--	if (!flags.recurse) {
-+	if (!state.flags.recurse) {
- 		if (check_excluded(pathname)) {
- 			error = 0;
- 			goto cleanup;
- 		}
- 
--		error = restorecon_sb(pathname, &sb, &flags);
-+		error = restorecon_sb(pathname, &sb, &state.flags, true);
- 		goto cleanup;
- 	}
- 
- 	/* Obtain fs type */
--	memset(&sfsb, 0, sizeof sfsb);
--	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &sfsb) < 0) {
-+	memset(&state.sfsb, 0, sizeof(state.sfsb));
-+	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) {
- 		selinux_log(SELINUX_ERROR,
- 			    "statfs(%s) failed: %m\n",
- 			    pathname);
-@@ -975,21 +1142,21 @@ int selinux_restorecon(const char *pathname_orig,
- 	}
- 
- 	/* Skip digest on in-memory filesystems and /sys */
--	if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC ||
--	    sfsb.f_type == SYSFS_MAGIC)
--		setrestorecondigest = false;
-+	if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC ||
-+	    state.sfsb.f_type == SYSFS_MAGIC)
-+		state.setrestorecondigest = false;
- 
--	if (flags.set_xdev)
-+	if (state.flags.set_xdev)
- 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
- 	else
- 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
- 
--	fts = fts_open(paths, fts_flags, NULL);
--	if (!fts)
-+	state.fts = fts_open(paths, fts_flags, NULL);
-+	if (!state.fts)
- 		goto fts_err;
- 
--	ftsent = fts_read(fts);
--	if (!ftsent)
-+	state.ftsent_first = fts_read(state.fts);
-+	if (!state.ftsent_first)
- 		goto fts_err;
- 
- 	/*
-@@ -1001,106 +1168,66 @@ int selinux_restorecon(const char *pathname_orig,
- 	 * directories with a different device number when the FTS_XDEV flag
- 	 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
- 	 */
--	dev_num = ftsent->fts_statp->st_dev;
-+	state.dev_num = state.ftsent_first->fts_statp->st_dev;
- 
--	error = 0;
--	do {
--		/* If the FTS_XDEV flag is set and the device is different */
--		if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num)
--			continue;
-+	if (nthreads == 1) {
-+		state.parallel = false;
-+		selinux_restorecon_thread(&state);
-+	} else {
-+		size_t i;
-+		pthread_t self = pthread_self();
-+		pthread_t *threads = NULL;
- 
--		switch (ftsent->fts_info) {
--		case FTS_DC:
--			selinux_log(SELINUX_ERROR,
--				    "Directory cycle on %s.\n",
--				    ftsent->fts_path);
--			errno = ELOOP;
--			error = -1;
--			goto out;
--		case FTS_DP:
--			continue;
--		case FTS_DNR:
--			errno_tmp = errno;
--			errno = ftsent->fts_errno;
--			selinux_log(SELINUX_ERROR,
--				    "Could not read %s: %m.\n",
--				    ftsent->fts_path);
--			errno = errno_tmp;
--			fts_set(fts, ftsent, FTS_SKIP);
--			continue;
--		case FTS_NS:
--			errno_tmp = errno;
--			errno = ftsent->fts_errno;
--			selinux_log(SELINUX_ERROR,
--				    "Could not stat %s: %m.\n",
--				    ftsent->fts_path);
--			errno = errno_tmp;
--			fts_set(fts, ftsent, FTS_SKIP);
--			continue;
--		case FTS_ERR:
--			errno_tmp = errno;
--			errno = ftsent->fts_errno;
--			selinux_log(SELINUX_ERROR,
--				    "Error on %s: %m.\n",
--				    ftsent->fts_path);
--			errno = errno_tmp;
--			fts_set(fts, ftsent, FTS_SKIP);
--			continue;
--		case FTS_D:
--			if (sfsb.f_type == SYSFS_MAGIC &&
--			    !selabel_partial_match(fc_sehandle,
--			    ftsent->fts_path)) {
--				fts_set(fts, ftsent, FTS_SKIP);
--				continue;
--			}
-+		pthread_mutex_init(&state.mutex, NULL);
- 
--			if (check_excluded(ftsent->fts_path)) {
--				fts_set(fts, ftsent, FTS_SKIP);
--				continue;
-+		threads = calloc(nthreads - 1, sizeof(*threads));
-+		if (!threads)
-+			goto oom;
-+
-+		state.parallel = true;
-+		/*
-+		 * Start (nthreads - 1) threads - the main thread is going to
-+		 * take part, too.
-+		 */
-+		for (i = 0; i < nthreads - 1; i++) {
-+			if (pthread_create(&threads[i], NULL,
-+					   selinux_restorecon_thread, &state)) {
-+				/*
-+				 * If any thread fails to be created, just mark
-+				 * it as such and let the successfully created
-+				 * threads do the job. In the worst case the
-+				 * main thread will do everything, but that's
-+				 * still better than to give up.
-+				 */
-+				threads[i] = self;
- 			}
-+		}
- 
--			if (setrestorecondigest) {
--				struct dir_hash_node *new_node = NULL;
-+		/* Let's join in on the fun! */
-+		selinux_restorecon_thread(&state);
- 
--				if (check_context_match_for_dir(ftsent->fts_path,
--								&new_node,
--								error) &&
--								!ignore_digest) {
--					selinux_log(SELINUX_INFO,
--						    "Skipping restorecon on directory(%s)\n",
--						    ftsent->fts_path);
--					fts_set(fts, ftsent, FTS_SKIP);
--					continue;
--				}
--
--				if (new_node && !error) {
--					if (!current) {
--						current = new_node;
--						head = current;
--					} else {
--						current->next = new_node;
--						current = current->next;
--					}
--				}
--			}
--			/* fall through */
--		default:
--			error |= restorecon_sb(ftsent->fts_path,
--					       ftsent->fts_statp, &flags);
--			if (flags.warnonnomatch)
--				flags.warnonnomatch = false;
--			if (error && flags.abort_on_error)
--				goto out;
--			break;
-+		/* Now wait for all threads to finish. */
-+		for (i = 0; i < nthreads - 1; i++) {
-+			/* Skip threads that failed to be created. */
-+			if (pthread_equal(threads[i], self))
-+				continue;
-+			pthread_join(threads[i], NULL);
- 		}
--	} while ((ftsent = fts_read(fts)) != NULL);
-+		free(threads);
-+
-+		pthread_mutex_destroy(&state.mutex);
-+	}
-+
-+	error = state.error;
-+	if (state.saved_errno)
-+		goto out;
- 
- 	/*
- 	 * Labeling successful. Write partial match digests for subdirectories.
- 	 * TODO: Write digest upon FTS_DP if no error occurs in its descents.
- 	 */
--	if (setrestorecondigest && !flags.nochange && !error) {
--		current = head;
-+	if (state.setrestorecondigest && !state.flags.nochange && !error) {
-+		current = state.head;
- 		while (current != NULL) {
- 			if (setxattr(current->path,
- 			    RESTORECON_PARTIAL_MATCH_DIGEST,
-@@ -1115,22 +1242,21 @@ int selinux_restorecon(const char *pathname_orig,
- 	}
- 
- out:
--	if (flags.progress && flags.mass_relabel)
-+	if (state.flags.progress && state.flags.mass_relabel)
- 		fprintf(stdout, "\r%s 100.0%%\n", pathname);
- 
--	sverrno = errno;
--	(void) fts_close(fts);
--	errno = sverrno;
-+	(void) fts_close(state.fts);
-+	errno = state.saved_errno;
- cleanup:
--	if (flags.add_assoc) {
--		if (flags.verbose)
-+	if (state.flags.add_assoc) {
-+		if (state.flags.verbose)
- 			filespec_eval();
- 		filespec_destroy();
- 	}
- 	free(pathdnamer);
- 	free(pathname);
- 
--	current = head;
-+	current = state.head;
- 	while (current != NULL) {
- 		struct dir_hash_node *next = current->next;
- 
-@@ -1164,6 +1290,26 @@ fts_err:
- 	goto cleanup;
- }
- 
-+
-+/*
-+ * Public API
-+ */
-+
-+/* selinux_restorecon(3) - Main function that is responsible for labeling */
-+int selinux_restorecon(const char *pathname_orig,
-+		       unsigned int restorecon_flags)
-+{
-+	return selinux_restorecon_common(pathname_orig, restorecon_flags, 1);
-+}
-+
-+/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */
-+int selinux_restorecon_parallel(const char *pathname_orig,
-+				unsigned int restorecon_flags,
-+				size_t nthreads)
-+{
-+	return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads);
-+}
-+
- /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
- void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
- {
-diff --git a/libselinux/src/selinuxswig_python.i b/libselinux/src/selinuxswig_python.i
-index 4c73bf92df96..17e03b9e36a5 100644
---- a/libselinux/src/selinuxswig_python.i
-+++ b/libselinux/src/selinuxswig_python.i
-@@ -20,7 +20,7 @@ DISABLED = -1
- PERMISSIVE = 0
- ENFORCING = 1
- 
--def restorecon(path, recursive=False, verbose=False, force=False):
-+def restorecon(path, recursive=False, verbose=False, force=False, nthreads=1):
-     """ Restore SELinux context on a given path
- 
-     Arguments:
-@@ -32,6 +32,8 @@ def restorecon(path, recursive=False, verbose=False, force=False):
-     force -- Force reset of context to match file_context for customizable files,
-     and the default file context, changing the user, role, range portion  as well
-     as the type (default False)
-+    nthreads -- The number of threads to use during relabeling, or 0 to use as many
-+    threads as there are online CPU cores (default 1)
-     """
- 
-     restorecon_flags = SELINUX_RESTORECON_IGNORE_DIGEST | SELINUX_RESTORECON_REALPATH
-@@ -41,7 +43,7 @@ def restorecon(path, recursive=False, verbose=False, force=False):
-         restorecon_flags |= SELINUX_RESTORECON_VERBOSE
-     if force:
-         restorecon_flags |= SELINUX_RESTORECON_SET_SPECFILE_CTX
--    selinux_restorecon(os.path.expanduser(path), restorecon_flags)
-+    selinux_restorecon_parallel(os.path.expanduser(path), restorecon_flags, nthreads)
- 
- def chcon(path, context, recursive=False):
-     """ Set the SELinux context on a given path """
-diff --git a/libselinux/src/selinuxswig_python_exception.i b/libselinux/src/selinuxswig_python_exception.i
-index 237ea69ad5f5..a02f4923a1e7 100644
---- a/libselinux/src/selinuxswig_python_exception.i
-+++ b/libselinux/src/selinuxswig_python_exception.i
-@@ -1183,6 +1183,14 @@
-   }
- }
- 
-+%exception selinux_restorecon_parallel {
-+  $action
-+  if (result < 0) {
-+     PyErr_SetFromErrno(PyExc_OSError);
-+     SWIG_fail;
-+  }
-+}
-+
- %exception selinux_restorecon_set_alt_rootpath {
-   $action
-   if (result < 0) {
--- 
-2.33.1
-
diff --git a/SOURCES/0009-libselinux-Fix-selinux_restorecon_parallel-symbol-ve.patch b/SOURCES/0009-libselinux-Fix-selinux_restorecon_parallel-symbol-ve.patch
deleted file mode 100644
index a8ce120..0000000
--- a/SOURCES/0009-libselinux-Fix-selinux_restorecon_parallel-symbol-ve.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 9456297275987dedefe2e8ad508360be9d9f9e7f Mon Sep 17 00:00:00 2001
-From: Petr Lautrbach <plautrba@redhat.com>
-Date: Tue, 23 Nov 2021 11:31:08 +0100
-Subject: [PATCH] libselinux: Fix selinux_restorecon_parallel symbol version
-
-selinux_restorecon_parallel was originally proposed before 3.3, but it
-was merged after release so it will be introduced in version 3.4.
-
-Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
----
- libselinux/src/libselinux.map | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map
-index d138e951ef0d..4acf1caacb55 100644
---- a/libselinux/src/libselinux.map
-+++ b/libselinux/src/libselinux.map
-@@ -241,7 +241,7 @@ LIBSELINUX_1.0 {
-     *;
- };
- 
--LIBSELINUX_3.3 {
-+LIBSELINUX_3.4 {
-   global:
-     selinux_restorecon_parallel;
- } LIBSELINUX_1.0;
--- 
-2.33.1
-
diff --git a/SPECS/libselinux.spec b/SPECS/libselinux.spec
index 0b26d37..026015f 100644
--- a/SPECS/libselinux.spec
+++ b/SPECS/libselinux.spec
@@ -1,30 +1,23 @@
 %define ruby_inc %(pkg-config --cflags ruby)
-%define libsepolver 3.3-1
+%define libsepolver 3.4-1
 
 Summary: SELinux library and simple utilities
 Name: libselinux
-Version: 3.3
-Release: 2%{?dist}
+Version: 3.4
+Release: 3%{?dist}
 License: Public Domain
 # https://github.com/SELinuxProject/selinux/wiki/Releases
-Source0: https://github.com/SELinuxProject/selinux/releases/download/3.3/libselinux-3.3.tar.gz
+Source0: https://github.com/SELinuxProject/selinux/releases/download/3.4/libselinux-3.4.tar.gz
 Source1: selinuxconlist.8
 Source2: selinuxdefcon.8
 Url: https://github.com/SELinuxProject/selinux/wiki
 # $ git clone https://github.com/fedora-selinux/selinux.git
 # $ cd selinux
-# $ git format-patch -N 3.3 -- libselinux
+# $ git format-patch -N 3.4 -- libselinux
 # $ i=1; for j in 00*patch; do printf "Patch%04d: %s\n" $i $j; i=$((i+1));done
 # Patch list start
 Patch0001: 0001-Use-SHA-2-instead-of-SHA-1.patch
-Patch0002: 0002-label_file-fix-a-data-race.patch
-Patch0003: 0003-selinux_restorecon-simplify-fl_head-allocation-by-us.patch
-Patch0004: 0004-selinux_restorecon-protect-file_spec-list-with-a-mut.patch
-Patch0005: 0005-libselinux-make-selinux_log-thread-safe.patch
-Patch0006: 0006-libselinux-make-is_context_customizable-thread-safe.patch
-Patch0007: 0007-selinux_restorecon-add-a-global-mutex-to-synchronize.patch
-Patch0008: 0008-selinux_restorecon-introduce-selinux_restorecon_para.patch
-Patch0009: 0009-libselinux-Fix-selinux_restorecon_parallel-symbol-ve.patch
+Patch0002: 0002-Revert-libselinux-restorecon-pin-file-to-avoid-TOCTO.patch
 # Patch list end
 BuildRequires: gcc make
 BuildRequires: ruby-devel ruby libsepol-static >= %{libsepolver} swig pcre2-devel xz-devel
@@ -221,6 +214,15 @@ rm -f %{buildroot}%{_mandir}/man8/togglesebool*
 %{ruby_vendorarchdir}/selinux.so
 
 %changelog
+* Mon Jul 18 2022 Petr Lautrbach <plautrba@redhat.com> - 3.4-3
+- Drop SHA-1 from selinux_restorecon.3
+
+* Tue May 31 2022 Petr Lautrbach <plautrba@redhat.com> - 3.4-2
+- Revert "libselinux: restorecon: pin file to avoid TOCTOU issues"
+
+* Thu May 19 2022 Petr Lautrbach <plautrba@redhat.com> - 3.4-1
+- SELinux userspace 3.4 release
+
 * Mon Nov 29 2021 Petr Lautrbach <plautrba@redhat.com> - 3.3-2
 - Introduce selinux_restorecon_parallel(3)