Patchwork Linux: Implement per-thread file system attributes

login
register
mail settings
Submitter Florian Weimer
Date June 7, 2019, 4:41 p.m.
Message ID <87d0jpfius.fsf@oldenburg2.str.redhat.com>
Download mbox | patch
Permalink /patch/33047/
State New
Headers show

Comments

Florian Weimer - June 7, 2019, 4:41 p.m.
[Compared to the previous version, this fixes the hppa build by updating
its own copy of pthread.h.  It also addresses the rebase conflicts due
to other new functions.  This is still the version with the “sticky”
behavior.]

This commit adds the functions pthread_attr_setperthreadfs_np and
pthread_attr_getperthreadfs_np.

The implementation is based on suppressing the CLONE_FS clone flag when
creating the new thread.  The new flag is sticky and is applied to
all threads created from a thread with the PTHREAD_PER_THREAD_NP
attribute.

2019-06-07  Florian Weimer  <fweimer@redhat.com>

	Linux: Implement per-thread file system attributes.
	* manual/threads.texi (Enabling Per-Thread Properties): New
	section.
	(Non-POSIX Extensions): Reference it.
	* nptl/Makefile (routines): Add pthread_attr_setperthreadfs_np,
	pthread_attr_getperthreadfs_np.
	(tests): Add tst-pthread-perthreadfs,
	tst-pthread-perthreadfs-chroot.
	* nptl/Versions (GLIBC_2.30): Export
	pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np.
	* nptl/pthread_attr_setperthreadfs_np.c: New file
	* nptl/pthread_attr_getperthreadfs_np.c: Likewise.
	* nptl/pthread_create.c (__pthread_create_2_1): Use
	ATTR_FLAGS_IGNORED_ATTR and ATTR_FLAGS_INHERITED to compute the
	flags for the new thread.
	* nptl/tst-pthread-perthreadfs.c: Likewise.
	* nptl/tst-pthread-perthreadfs-chroot.c: Likewise.
	* sysdeps/nptl/internaltypes.h (ATTR_FLAG_PERTHREADFS)
	(ATTR_FLAGS_IGNORED_ATTR, ATTR_FLAGS_INHERITED): Define.
	* sysdeps/unix/sysv/linux/createthread.c (create_thread): Use it.
	* sysdeps/nptl/pthread.h
	(PTHREAD_PER_PROCESS_NP, PTHREAD_PER_THREAD_NP): Define.
	(pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np):
	Declare.
	* sysdeps/unix/sysv/linux/hppa/pthread.h
	(PTHREAD_PER_PROCESS_NP, PTHREAD_PER_THREAD_NP): Define.
	(pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np):
	Declare.
	* support/Makefile (libsupport-routines): Add xchdir, xfchdir.
	* support/xunistd.h (xchdir, xfchdir): Declare.
	* support/xchdir.c: New file.
	* support/xfchdir.c: Likewise.
	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30): Add
	pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
	Likewise.

Patch

diff --git a/NEWS b/NEWS
index 00f9e855a2..f320675f7c 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,11 @@  Major new features:
   pointer subtraction within the allocated object, where results might
   overflow the ptrdiff_t type.
 
+* On Linux, it is now possible to create threads with per-thread current
+  directory, umask, and file system root.  The functions
+  pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
+  been added in support of that.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The functions clock_gettime, clock_getres, clock_settime,
diff --git a/manual/threads.texi b/manual/threads.texi
index 87fda7d8e7..60c15a8d15 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -625,6 +625,7 @@  the standard.
 @menu
 * Default Thread Attributes::             Setting default attributes for
 					  threads in a process.
+* Enabling Per-Thread Properties::        Additional per-thread properties.
 @end menu
 
 @node Default Thread Attributes
@@ -669,6 +670,100 @@  The system does not have sufficient memory.
 @end table
 @end deftypefun
 
+@node Enabling Per-Thread Properties
+@subsubsection Enabling Additional Per-Thread Properties
+
+POSIX mandates that the current directory, file system root, umask
+value, and the current user and group IDs are process-global
+properties.  For example, if a thread calls the @code{chdir} function
+to change to a different directory, all threads are eventually
+affected by this change.  @Theglibc{} implements an extension which
+allows threads to be created which do not share these properties with
+the rest of the process.
+
+The desired behavior is specified at the time the thread is created,
+using the thread attribute.  The following constants are used to
+update the attribute:
+
+@vtable @code
+@item PTHREAD_PER_PROCESS_NP
+@standards{GNU, pthread.h}
+This property in question is globally shared across the entire process.
+This is the default.
+
+@item PTHREAD_PER_THREAD_NP
+@standards{GNU, pthread.h}
+This property in question is thread-specific.
+@end vtable
+
+The @code{PTHREAD_PER_THREAD_NP} flag is sticky, in the sense that all
+threads created by a thread created with this flag have per-thread
+properties, even if they are created with the matching thread
+attribute set to the @code{PTHREAD_PER_PROCESS_NP} flag.  If an
+application wants to create new threads sharing properties with the
+main thread, it should create a service thread early (perhaps from an
+ELF constructor) and create these threads using this service thread.
+
+Per-thread properties can be set and examined for an attribute using
+the functions below.
+
+@deftypefun int pthread_attr_setperthreadfs_np (pthread_attr_t *@var{attr}, int @var{scope})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Change whether the following properties related to file system access
+are made thread-specific when a new thread is created using the
+attribute @var{attr}:
+
+@itemize @bullet
+@item
+@cindex current directiry as a per-thread property
+@cindex per-thread current directory
+@cindex thread-specific current directory
+current directory (as changed by @code{chdir} and related functions)
+
+@item
+@cindex @code{chroot} as a per-thread property
+@cindex per-thread @code{chroot}
+@cindex thread-specific @code{chroot}
+file system root (as changed by @code{chroot})
+
+@item
+@cindex umask as a per-thread property
+@cindex per-thread @code{umask}
+@cindex thread-specific @code{umask}
+umask value (as changed by the function of the same name)
+@end itemize
+
+This function returns zero on success.  @var{scope} must be one of the
+constants @code{PTHREAD_PER_PROCESS_NP} or
+@code{PTHREAD_PER_THREAD_NP}, otherwise the function returns
+@code{EINVAL}.
+
+If @var{scope} is @code{PTHREAD_PER_THREAD_NP}, the attribute will
+cause the properties listed above to be specific to the thread.  The
+initial values of these properties are copied from the creating
+thread, at thread creation time.
+
+If a thread that has been created with the
+@code{PTHREAD_PER_THREAD_NP} flag creates further threads, these
+threads are implicitly created with the @code{PTHREAD_PER_THREAD_NP}
+flag, ignoring the value of this thread creation attribute.
+(If this behavior is not desirable, it is possible to call
+@samp{unshare (CLONE_FS)} from the new thread instead of creating it
+with the @code{PTHREAD_PER_THREAD_NP} flag.)
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
+@deftypefun int pthread_attr_getperthreadfs_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Obtain the per-thread status of the file system properties in
+@var{attr} and store it in the location @var{scope}.
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index de312b3477..1374838339 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -30,7 +30,8 @@  extra-libs-others := $(extra-libs)
 routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
 	   libc-cleanup libc_pthread_init libc_multiple_threads \
 	   register-atfork pthread_atfork pthread_self thrd_current \
-	   thrd_equal thrd_sleep thrd_yield
+	   thrd_equal thrd_sleep thrd_yield \
+	   pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np
 shared-only-routines = forward
 static-only-routines = pthread_atfork
 
@@ -321,7 +322,8 @@  tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
 	tst-rwlock-pwn \
 	tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
-	tst-unwind-thread
+	tst-unwind-thread \
+	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot
 
 tests-internal := tst-rwlock19 tst-rwlock20 \
 		  tst-sem11 tst-sem12 tst-sem13 \
diff --git a/nptl/Versions b/nptl/Versions
index e7f691da7a..817fec04f3 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -32,6 +32,10 @@  libc {
   GLIBC_2.28 {
     thrd_current; thrd_equal; thrd_sleep; thrd_yield;
   }
+  GLIBC_2.30 {
+    pthread_attr_setperthreadfs_np;
+    pthread_attr_getperthreadfs_np;
+  }
   GLIBC_PRIVATE {
     __libc_alloca_cutoff;
     # Internal libc interface to libpthread
diff --git a/nptl/pthread_attr_getperthreadfs_np.c b/nptl/pthread_attr_getperthreadfs_np.c
new file mode 100644
index 0000000000..6670939101
--- /dev/null
+++ b/nptl/pthread_attr_getperthreadfs_np.c
@@ -0,0 +1,29 @@ 
+/* Read the private file system flag in thread attributes.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <internaltypes.h>
+
+int
+pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict attr,
+                                int *__restrict scope)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  *scope = (iattr->flags & ATTR_FLAG_PERTHREADFS) != 0;
+  return 0;
+}
diff --git a/nptl/pthread_attr_setperthreadfs_np.c b/nptl/pthread_attr_setperthreadfs_np.c
new file mode 100644
index 0000000000..2d274cb144
--- /dev/null
+++ b/nptl/pthread_attr_setperthreadfs_np.c
@@ -0,0 +1,39 @@ 
+/* Change the private file system flag in thread attributes.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <internaltypes.h>
+#include <errno.h>
+
+int
+pthread_attr_setperthreadfs_np (pthread_attr_t *attr, int scope)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  switch (scope)
+    {
+    case PTHREAD_PER_PROCESS_NP:
+      iattr->flags &= ~ATTR_FLAG_PERTHREADFS;
+      return 0;
+      break;
+    case PTHREAD_PER_THREAD_NP:
+      iattr->flags |= ATTR_FLAG_PERTHREADFS;
+      return 0;
+    default:
+      return EINVAL;
+    }
+}
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 18b7bbe765..0002fa7299 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -693,8 +693,8 @@  __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
 
   /* Copy the thread attribute flags.  */
   struct pthread *self = THREAD_SELF;
-  pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
-	       | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
+  pd->flags = ((iattr->flags & ~ATTR_FLAGS_IGNORED_ATTR)
+	       | (self->flags & ATTR_FLAGS_INHERITED));
 
   /* Initialize the field for the ID of the thread which is waiting
      for us.  This is a self-reference in case the thread is created
diff --git a/nptl/tst-pthread-perthreadfs-chroot.c b/nptl/tst-pthread-perthreadfs-chroot.c
new file mode 100644
index 0000000000..094c291baa
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs-chroot.c
@@ -0,0 +1,255 @@ 
+/* Test per-thread file system attributes, chroot version.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This thread is separate from tst-pthread-perthreadfs because it
+   requires chroot support.  */
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Paths for chroot operations.  */
+static char *chroot_1;
+static char *chroot_2;
+static char *chroot_3;
+
+/* These paths are used for recognizing chroots.  */
+static const char *const chroot_1_marker = "/chroot-1-marker";
+static const char *const chroot_2_marker = "/chroot-2-marker";
+
+/* Directory available in the chroot for a second chroot call.  */
+static const char *const next_chroot = "/next_chroot";
+
+/* Return 0 for no chroot, 1 for first chroot, 2 for second chroot, or
+   -1 for error.  */
+static int
+which_chroot (void)
+{
+  /* If the full (out-of-chroot) path is present, we are not in a
+     chroot.  */
+  if (access (chroot_1, F_OK) == 0)
+    return 0;
+  if (access (chroot_1_marker, F_OK) == 0)
+    return 1;
+  if (access (chroot_2_marker, F_OK) == 0)
+    return 2;
+  return -1;
+}
+
+/* Thread attribute requesting per-thread file system attributes.  */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads.  This is needed
+   so that the chroot changes happen in the expected order.  */
+static pthread_barrier_t barrier;
+
+static void *
+thread_which_chroot_helper (void *closure)
+{
+  int *result = closure;
+  *result = which_chroot ();
+  return NULL;
+}
+
+static void *
+thread_which_chroot_helper_perthread (void *closure)
+{
+  int *result = closure;
+  *result = which_chroot ();
+  if (*result == 0)
+    /* No /next-chroot available without a first chroot.  */
+    xchroot (chroot_3);
+  else
+    xchroot (next_chroot);
+  return NULL;
+}
+
+/* Determine the current chroot on another thread.  */
+static int
+thread_which_chroot (void)
+{
+  int result1;
+  pthread_t thr = xpthread_create (NULL, thread_which_chroot_helper, &result1);
+  xpthread_join (thr);
+  /* Same using per-thread attributes.  They are supposed to be equal.
+     Also change the chroot to check isolation.  */
+  int result2;
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_which_chroot_helper_perthread, &result2);
+  xpthread_join (thr);
+  TEST_COMPARE (result1, result2);
+  return result1;
+}
+
+/* Verify that the file system attributes for the current thread are
+   as expected.  */
+static void
+check_attributes (const char *where, int expected_chroot)
+{
+  printf ("info: reading file attributes in %s\n", where);
+  TEST_COMPARE (which_chroot (), expected_chroot);
+  TEST_COMPARE (thread_which_chroot (), expected_chroot);
+}
+
+/* Thread function launched with per-thread file system
+   attributes.  */
+static void *
+thread_perthreadfs (void *closure)
+{
+  puts ("info: changing file attributes in thread_perthreadfs");
+  xchroot (chroot_1);
+  check_attributes ("thread_perthreadfs", 1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes changed in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_perthreadfs", 1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+/* Thread function launched with shared file system attributes.  */
+static void *
+thread_sharedfs (void *closure)
+{
+  /* Wait for thread_perthreadfs to update chroot.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_sharedfs", 0);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+
+  puts ("info: changing file attributes in thread_sharedfs");
+  xchroot (chroot_2);
+  check_attributes ("thread_sharedfs", 2);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    FAIL_UNSUPPORTED ("chroot not supported");
+
+  /* Used to revert the effect of chroot.  */
+  int original_chroot_fd = xopen ("/", O_DIRECTORY | O_RDONLY, 0);
+
+  TEST_COMPARE (access (chroot_1_marker, F_OK), -1);
+  TEST_COMPARE (access (chroot_2_marker, F_OK), -1);
+  TEST_COMPARE (access (next_chroot, F_OK), -1);
+
+  chroot_1 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-1-");
+  chroot_2 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-2-");
+  chroot_3 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-3-");
+  {
+    char *path = xasprintf ("%s%s", chroot_1, chroot_1_marker);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_1, next_chroot);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_2, chroot_2_marker);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_2, next_chroot);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+  }
+  TEST_COMPARE (which_chroot (), 0);
+
+  xpthread_barrier_init (&barrier, NULL, 3);
+  xpthread_attr_init (&attr_perthreadfs);
+  TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+                                                PTHREAD_PER_THREAD_NP), 0);
+
+  pthread_t thr1 = xpthread_create (&attr_perthreadfs,
+                                    thread_perthreadfs, NULL);
+  pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+
+  /* Wait for thread_perthreadfs to update chroot.  */
+  xpthread_barrier_wait (&barrier);
+  /* File attributes read thread_sharedfs here.  */
+  xpthread_barrier_wait (&barrier);
+
+  check_attributes ("main thread", 0);
+
+  xpthread_barrier_wait (&barrier);
+  /* File attributes changed thread_sharedfs here.  */
+  xpthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  xpthread_barrier_wait (&barrier);
+
+  check_attributes ("main thread", 2);
+
+  xpthread_barrier_wait (&barrier);
+
+  xpthread_join (thr2);
+  xpthread_join (thr1);
+
+  xpthread_attr_destroy (&attr_perthreadfs);
+  xpthread_barrier_destroy (&barrier);
+
+  free (chroot_3);
+  free (chroot_2);
+  free (chroot_1);
+
+  xfchdir (original_chroot_fd);
+  xclose (original_chroot_fd);
+  xchroot (".");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nptl/tst-pthread-perthreadfs.c b/nptl/tst-pthread-perthreadfs.c
new file mode 100644
index 0000000000..81cf315c97
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs.c
@@ -0,0 +1,280 @@ 
+/* Test per-thread file system attributes.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Original values in the parent process.  */
+static char *original_cwd;
+static mode_t original_umask;
+
+/* New values for the attributes, distinct from the old.  */
+static char *new_cwd_1;
+static char *new_cwd_2;
+static mode_t new_umask_1;
+static mode_t new_umask_2;
+
+/* Thread attribute requesting per-thread file system attributes.  */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads.  This is needed
+   so that the attribute changes happen in the expected order, and
+   that get_umask_unlocked can be used safely.  */
+static pthread_barrier_t barrier;
+
+/* Linux does not have getumask, so use synchronization and the umask
+   system call to read the value.  */
+static mode_t
+get_umask_unlocked (void)
+{
+  mode_t mask = umask (0);
+  TEST_COMPARE (umask (mask), 0);
+  return mask;
+}
+
+static void *
+thread_get_pwd_helper (void *closure)
+{
+  char *result = get_current_dir_name ();
+  if (closure != NULL)
+    xchdir (closure);
+  return result;
+}
+
+/* Obtain the current directory on another thread.  */
+static char *
+thread_get_pwd (void)
+{
+  pthread_t thr = xpthread_create (NULL, thread_get_pwd_helper, NULL);
+  char *path1 = xpthread_join (thr);
+  /* Same using per-thread attributes.  They are supposed to be equal.
+     Also change the current directory to check isolation.  */
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_get_pwd_helper, (char *) "/");
+  char *path2 = xpthread_join (thr);
+  TEST_COMPARE_STRING (path1, path2);
+  free (path2);
+  return path1;
+}
+
+static void *
+thread_get_umask_helper (void *closure)
+{
+  mode_t *pmask = closure;
+  *pmask = get_umask_unlocked ();
+  return NULL;
+}
+
+static void *
+thread_get_umask_helper_perthread (void *closure)
+{
+  mode_t *pmask = closure;
+  *pmask = get_umask_unlocked ();
+  /* Check isolation.  This bit pattern is not used anywhere else.  */
+  TEST_COMPARE (umask (*pmask ^ 0101), *pmask);
+  return NULL;
+}
+
+/* Obtain the current umask on another thread.  */
+static mode_t
+thread_get_umask_unlocked (void)
+{
+  mode_t mask1;
+  pthread_t thr = xpthread_create (NULL, thread_get_umask_helper, &mask1);
+  xpthread_join (thr);
+  mode_t mask2;
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_get_umask_helper_perthread, &mask2);
+  xpthread_join (thr);
+  TEST_COMPARE (mask1, mask2);
+  return mask1;
+}
+
+/* Verify that the file system attributes for the current thread are
+   as expected.  */
+static void
+check_attributes (const char *where, mode_t expected_umask,
+                  const char *expected_cwd)
+{
+  printf ("info: reading file attributes in %s\n", where);
+  TEST_COMPARE (get_umask_unlocked (), expected_umask);
+  TEST_COMPARE (thread_get_umask_unlocked (), expected_umask);
+  char *cwd = get_current_dir_name ();
+  TEST_COMPARE_STRING (cwd, expected_cwd);
+  free (cwd);
+  cwd = thread_get_pwd ();
+  TEST_COMPARE_STRING (cwd, expected_cwd);
+  free (cwd);
+}
+
+/* Thread function launched with per-thread file system
+   attributes.  */
+static void *
+thread_perthreadfs (void *closure)
+{
+  puts ("info: changing file attributes in thread_perthreadfs");
+  xchdir (new_cwd_1);
+  TEST_COMPARE (umask (new_umask_1), original_umask);
+  check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes changed in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+/* Launched with te PTHREAD_PER_THREAD_NP, but runs the actual test on
+   a PTHREAD_PER_PROCESS_NP thread.  */
+static void *
+thread_perthreadfs_indirect (void *closure)
+{
+  return xpthread_join (xpthread_create (NULL, thread_perthreadfs, closure));
+}
+
+/* Thread function launched with shared file system attributes.  */
+static void *
+thread_sharedfs (void *closure)
+{
+  /* Wait for thread_perthreadfs to update current directory and
+     umask.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_sharedfs", original_umask, original_cwd);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+
+  puts ("info: changing file attributes in thread_sharedfs");
+  TEST_COMPARE (umask (new_umask_2), original_umask);
+  xchdir (new_cwd_2);
+  check_attributes ("thread_sharedfs", new_umask_2, new_cwd_2);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  original_cwd = get_current_dir_name ();
+  TEST_VERIFY_EXIT (original_cwd != NULL);
+  original_umask = get_umask_unlocked ();
+
+  new_cwd_1 = support_create_temp_directory ("tst-pthread-perthreadfs-1-");
+  new_cwd_2 = support_create_temp_directory ("tst-pthread-perthreadfs-2-");
+  /* Arbitrary bit pattern change to obtain distinct values, so that
+     it is possible to check for actual changes.  */
+  new_umask_1 = original_umask ^ 0111;
+  new_umask_2 = original_umask ^ 0222;
+
+  xpthread_barrier_init (&barrier, NULL, 3);
+  xpthread_attr_init (&attr_perthreadfs);
+  {
+    int scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_PROCESS_NP);
+    TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+                                                  PTHREAD_PER_THREAD_NP), 0);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+
+    TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs, 2),
+                  EINVAL);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+  }
+
+  /* Two test runs, once with perthreadsfs set to
+     PTHREAD_PER_THREAD_NP directly, once indirectly via inheritance
+     from the current (overriding the PTHREAD_PER_PROCESS_NP
+     default).  */
+  for (int do_indirect = 0; do_indirect < 2;  ++do_indirect)
+    {
+      printf ("info: test iteration with do_indirect == %d\n", do_indirect);
+      pthread_t thr1;
+      if (do_indirect)
+        thr1 = xpthread_create (&attr_perthreadfs,
+                                thread_perthreadfs_indirect, NULL);
+      else
+        thr1 = xpthread_create (&attr_perthreadfs, thread_perthreadfs, NULL);
+      pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+
+      /* Wait for thread_perthreadfs to update current directory and
+         umask.  */
+      xpthread_barrier_wait (&barrier);
+      /* File attributes read thread_sharedfs here.  */
+      xpthread_barrier_wait (&barrier);
+
+      check_attributes ("main thread", original_umask, original_cwd);
+
+      xpthread_barrier_wait (&barrier);
+      /* File attributes changed thread_sharedfs here.  */
+      xpthread_barrier_wait (&barrier);
+      /* File attributes read in thread_perthreadfs here.  */
+      xpthread_barrier_wait (&barrier);
+
+      check_attributes ("main thread", new_umask_2, new_cwd_2);
+
+      xpthread_barrier_wait (&barrier);
+
+      xpthread_join (thr2);
+      xpthread_join (thr1);
+
+      xchdir (original_cwd);
+      umask (original_umask);
+    }
+
+  xpthread_attr_destroy (&attr_perthreadfs);
+  xpthread_barrier_destroy (&barrier);
+
+  free (new_cwd_2);
+  free (new_cwd_1);
+  free (original_cwd);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/Makefile b/support/Makefile
index 56c1ed43bb..774b0a692a 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -80,6 +80,7 @@  libsupport-routines = \
   xasprintf \
   xbind \
   xcalloc \
+  xchdir \
   xchroot \
   xclock_gettime \
   xclose \
@@ -88,6 +89,7 @@  libsupport-routines = \
   xdlfcn \
   xdlmopen \
   xdup2 \
+  xfchdir \
   xfclose \
   xfopen \
   xfork \
diff --git a/support/xchdir.c b/support/xchdir.c
new file mode 100644
index 0000000000..a2f728f5df
--- /dev/null
+++ b/support/xchdir.c
@@ -0,0 +1,28 @@ 
+/* chdir with error checking.
+   Copyright (C) 2017-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xchdir (const char *path)
+{
+  if (chdir (path) != 0)
+    FAIL_EXIT1 ("chdir (\"%s\"): %m", path);
+}
diff --git a/support/xfchdir.c b/support/xfchdir.c
new file mode 100644
index 0000000000..76c5f655bb
--- /dev/null
+++ b/support/xfchdir.c
@@ -0,0 +1,28 @@ 
+/* fchdir with error checking.
+   Copyright (C) 2017-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xfchdir (int fd)
+{
+  if (fchdir (fd) != 0)
+    FAIL_EXIT1 ("fchdir (%d): %m", fd);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 338eb86a1b..b470d99be1 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -38,6 +38,8 @@  int xopen (const char *path, int flags, mode_t);
 void xstat (const char *path, struct stat64 *);
 void xfstat (int fd, struct stat64 *);
 void xmkdir (const char *path, mode_t);
+void xchdir (const char *path);
+void xfchdir (int);
 void xchroot (const char *path);
 void xunlink (const char *path);
 long xsysconf (int name);
diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h
index 53d037e0f1..5ad56908b5 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -48,6 +48,18 @@  struct pthread_attr
 #define ATTR_FLAG_OLDATTR		0x0010
 #define ATTR_FLAG_SCHED_SET		0x0020
 #define ATTR_FLAG_POLICY_SET		0x0040
+#define ATTR_FLAG_PERTHREADFS		0x0080
+
+/* These flags are not copied from the thread attribute at
+   pthread_create time.  */
+#define ATTR_FLAGS_IGNORED_ATTR \
+  (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)
+
+/* These flags are inherited from the current thread during
+   pthread_create even if they are not specified in the thread
+   attribute.  */
+#define ATTR_FLAGS_INHERITED \
+  ATTR_FLAG_PERTHREADFS
 
 
 /* Mutex attribute data structure.  */
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 704a3c48d6..37073e3ce7 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -181,7 +181,16 @@  enum
 #define PTHREAD_PROCESS_SHARED  PTHREAD_PROCESS_SHARED
 };
 
-
+#ifdef __USE_GNU
+/* Thread-private or process-global flag.  */
+enum
+{
+ PTHREAD_PER_PROCESS_NP,
+# define PTHREAD_PER_PROCESS_NP PTHREAD_PER_PROCESS_NP
+ PTHREAD_PER_THREAD_NP
+# define PTHREAD_PER_THREAD_NP PTHREAD_PER_THREAD_NP
+};
+#endif /* __USE_GNU */
 
 /* Conditional variable handling.  */
 #define PTHREAD_COND_INITIALIZER { { {0}, {0}, {0, 0}, {0, 0}, 0, 0, {0, 0} } }
@@ -406,6 +415,21 @@  extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr,
 					cpu_set_t *__cpuset)
      __THROW __nonnull ((1, 3));
 
+
+/* Control the flag in ATTR whether the thread has its own current
+   directory, file system root, and umask attribute.  SCOPE must be
+   PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP.  By default, new
+   threads share these attributes with their creating thread
+   (PTHREAD_PER_PROCESS_NP).  */
+int pthread_attr_setperthreadfs_np (pthread_attr_t *__attr, int __scope)
+  __THROW __nonnull ((1));
+
+/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
+   depending on the state of *ATTR.  */
+int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
+				    int *__restrict __scope)
+  __THROW __nonnull ((1, 2));
+
 /* Get the default attributes used by pthread_create in this process.  */
 extern int pthread_getattr_default_np (pthread_attr_t *__attr)
      __THROW __nonnull ((1));
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index a4c31932cb..2635e16f5e 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2143,5 +2143,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fe85a35620..12227b9800 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2218,6 +2218,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index bc3df8dcea..6d09148e1d 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -128,6 +128,8 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
index 579bd94743..177e14e9ae 100644
--- a/sysdeps/unix/sysv/linux/createthread.c
+++ b/sysdeps/unix/sysv/linux/createthread.c
@@ -66,7 +66,9 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
 
      CLONE_VM, CLONE_FS, CLONE_FILES
 	These flags select semantics with shared address space and
-	file descriptors according to what POSIX requires.
+	file descriptors according to what POSIX requires.  CLONE_FS
+	is optional; it can be controlled using the function
+	pthread_attr_setperthreadfs_np.
 
      CLONE_SIGHAND, CLONE_THREAD
 	This flag selects the POSIX signal semantics and various
@@ -90,11 +92,13 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
 
      The termination signal is chosen to be zero which means no signal
      is sent.  */
-  const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM
-			   | CLONE_SIGHAND | CLONE_THREAD
-			   | CLONE_SETTLS | CLONE_PARENT_SETTID
-			   | CLONE_CHILD_CLEARTID
-			   | 0);
+  int clone_flags = (CLONE_VM | CLONE_FILES | CLONE_SYSVSEM
+		     | CLONE_SIGHAND | CLONE_THREAD
+		     | CLONE_SETTLS | CLONE_PARENT_SETTID
+		     | CLONE_CHILD_CLEARTID
+		     | 0);
+  if ((attr->flags & ATTR_FLAG_PERTHREADFS) == 0)
+    clone_flags |= CLONE_FS;
 
   TLS_DEFINE_INIT_TP (tp, pd);
 
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 9b3cee65bb..5ee091972b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2087,5 +2087,7 @@  GLIBC_2.29 xprt_register F
 GLIBC_2.29 xprt_unregister F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 75edece94a..844eaf539b 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2039,6 +2039,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/hppa/pthread.h b/sysdeps/unix/sysv/linux/hppa/pthread.h
index 45e706c037..b80c84c0b7 100644
--- a/sysdeps/unix/sysv/linux/hppa/pthread.h
+++ b/sysdeps/unix/sysv/linux/hppa/pthread.h
@@ -158,6 +158,16 @@  enum
 #define PTHREAD_PROCESS_SHARED  PTHREAD_PROCESS_SHARED
 };
 
+#ifdef __USE_GNU
+/* Thread-private or process-global flag.  */
+enum
+{
+ PTHREAD_PER_PROCESS_NP,
+# define PTHREAD_PER_PROCESS_NP PTHREAD_PER_PROCESS_NP
+ PTHREAD_PER_THREAD_NP
+# define PTHREAD_PER_THREAD_NP PTHREAD_PER_THREAD_NP
+};
+#endif /* __USE_GNU */
 
 /* Conditional variable handling.  */
 #define PTHREAD_COND_INITIALIZER { { {0}, {0}, {0, 0}, {0, 0}, 0, 0, {0, 0} } }
@@ -382,6 +392,21 @@  extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr,
 					cpu_set_t *__cpuset)
      __THROW __nonnull ((1, 3));
 
+
+/* Control the flag in ATTR whether the thread has its own current
+   directory, file system root, and umask attribute.  SCOPE must be
+   PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP.  By default, new
+   threads share these attributes with their creating thread
+   (PTHREAD_PER_PROCESS_NP).  */
+int pthread_attr_setperthreadfs_np (pthread_attr_t *__attr, int __scope)
+  __THROW __nonnull ((1));
+
+/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
+   depending on the state of *ATTR.  */
+int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
+				    int *__restrict __scope)
+  __THROW __nonnull ((1, 2));
+
 /* Get the default attributes used by pthread_create in this process.  */
 extern int pthread_getattr_default_np (pthread_attr_t *__attr)
      __THROW __nonnull ((1));
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index edeaf8e722..efd8cb1685 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2205,6 +2205,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index b5d460eeb2..7f34f4d1e6 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2071,6 +2071,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 05633b3cb8..34d5f6c91a 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -129,6 +129,8 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 47eb7b4608..71882c6e23 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2148,6 +2148,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index f7ced487f7..4f605fa67a 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2135,5 +2135,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index e49dc4272e..62790b0a64 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2122,6 +2122,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index daa3b60c5b..eb2ae61601 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2120,6 +2120,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 457ce0b6f2..9cf1462270 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2128,6 +2128,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 63d5c03bfb..d116e7180e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2122,6 +2122,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 7fec0c9670..0e8d5bfcc7 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2176,5 +2176,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 9200a54309..16e66c2fa4 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2178,6 +2178,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ef7779905f..fff0295a63 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2211,6 +2211,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 2860df8ebc..808f021335 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2041,6 +2041,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 2229a1dcc0..a894bc49be 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2245,5 +2245,7 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 31010e6cf7..e220b0fd0c 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2105,5 +2105,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 576295deff..1567d2ff1d 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2173,6 +2173,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index abf0473683..f9d88d588e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2077,6 +2077,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 41977f6e9c..affd74df4c 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2043,6 +2043,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 3d2f00ca52..c12cc83bb2 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2167,6 +2167,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 2f20643e8e..37c9dff44c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2094,6 +2094,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 59f85d9373..71b7cc4ff9 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2052,6 +2052,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 67a4e238d6..573fc2e01c 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2151,5 +2151,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F