- Issue #15238: shutil.copystat now copies Linux "extended attributes".
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 6d80fee..2b2a9be5 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -132,6 +132,27 @@
     st = stat_func(src)
     chmod_func(dst, stat.S_IMODE(st.st_mode))
 
+if hasattr(os, 'listxattr'):
+    def _copyxattr(src, dst, symlinks=False):
+        """Copy extended filesystem attributes from `src` to `dst`.
+
+        Overwrite existing attributes.
+
+        If the optional flag `symlinks` is set, symlinks won't be followed.
+
+        """
+
+        for name in os.listxattr(src, follow_symlinks=symlinks):
+            try:
+                value = os.getxattr(src, name, follow_symlinks=symlinks)
+                os.setxattr(dst, name, value, follow_symlinks=symlinks)
+            except OSError as e:
+                if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
+                    raise
+else:
+    def _copyxattr(*args, **kwargs):
+        pass
+
 def copystat(src, dst, symlinks=False):
     """Copy all stat info (mode bits, atime, mtime, flags) from src to dst.
 
@@ -184,27 +205,7 @@
                     break
             else:
                 raise
-
-if hasattr(os, 'listxattr'):
-    def _copyxattr(src, dst, symlinks=False):
-        """Copy extended filesystem attributes from `src` to `dst`.
-
-        Overwrite existing attributes.
-
-        If the optional flag `symlinks` is set, symlinks won't be followed.
-
-        """
-
-        for name in os.listxattr(src, follow_symlinks=symlinks):
-            try:
-                value = os.getxattr(src, name, follow_symlinks=symlinks)
-                os.setxattr(dst, name, value, follow_symlinks=symlinks)
-            except OSError as e:
-                if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
-                    raise
-else:
-    def _copyxattr(*args, **kwargs):
-        pass
+    _copyxattr(src, dst, symlinks=follow)
 
 def copy(src, dst, symlinks=False):
     """Copy data and mode bits ("cp src dst"). Return the file's destination.
@@ -235,7 +236,6 @@
         dst = os.path.join(dst, os.path.basename(src))
     copyfile(src, dst, symlinks=symlinks)
     copystat(src, dst, symlinks=symlinks)
-    _copyxattr(src, dst, symlinks=symlinks)
     return dst
 
 def ignore_patterns(*patterns):
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index a2b6e88..cbbc36f 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -410,6 +410,16 @@
         finally:
             os.setxattr = orig_setxattr
 
+        # test that shutil.copystat copies xattrs
+        src = os.path.join(tmp_dir, 'the_original')
+        write_file(src, src)
+        os.setxattr(src, 'user.the_value', b'fiddly')
+        dst = os.path.join(tmp_dir, 'the_copy')
+        write_file(dst, dst)
+        shutil.copystat(src, dst)
+        self.assertEqual(os.listxattr(src), ['user.the_value'])
+        self.assertEqual(os.getxattr(src, 'user.the_value'), b'fiddly')
+
     @support.skip_unless_symlink
     @support.skip_unless_xattr
     @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,