Issue #14127: Add ns= parameter to utime, futimes, and lutimes.
Removed futimens as it is now redundant.
Changed shutil.copystat to use st_atime_ns and st_mtime_ns from os.stat
and ns= parameter to utime--it once again preserves exact metadata on Linux!
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 0ac7a49..6df4924 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -154,7 +154,7 @@
 
     st = stat_func(src)
     mode = stat.S_IMODE(st.st_mode)
-    utime_func(dst, (st.st_atime, st.st_mtime))
+    utime_func(dst, ns=(st.st_atime_ns, st.st_mtime_ns))
     chmod_func(dst, mode)
     if hasattr(st, 'st_flags'):
         try:
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index aa619a8..0c15f22 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -192,11 +192,11 @@
                 self.assertIn(attr, members)
 
         # Make sure that the st_?time and st_?time_ns fields roughly agree
-        # (they should always agree up to the tens-of-microseconds magnitude)
+        # (they should always agree up to around tens-of-microseconds)
         for name in 'st_atime st_mtime st_ctime'.split():
             floaty = int(getattr(result, name) * 100000)
             nanosecondy = getattr(result, name + "_ns") // 10000
-            self.assertEqual(floaty, nanosecondy)
+            self.assertAlmostEqual(floaty, nanosecondy, delta=2)
 
         try:
             result[200]
@@ -303,20 +303,80 @@
         st2 = os.stat(support.TESTFN)
         self.assertEqual(st2.st_mtime, int(st.st_mtime-delta))
 
-    def test_utime_noargs(self):
+    def _test_utime(self, filename, attr, utime, delta):
         # Issue #13327 removed the requirement to pass None as the
         # second argument. Check that the previous methods of passing
         # a time tuple or None work in addition to no argument.
-        st = os.stat(support.TESTFN)
+        st0 = os.stat(filename)
         # Doesn't set anything new, but sets the time tuple way
-        os.utime(support.TESTFN, (st.st_atime, st.st_mtime))
+        utime(filename, (attr(st0, "st_atime"), attr(st0, "st_mtime")))
+        # Setting the time to the time you just read, then reading again,
+        # should always return exactly the same times.
+        st1 = os.stat(filename)
+        self.assertEqual(attr(st0, "st_mtime"), attr(st1, "st_mtime"))
+        self.assertEqual(attr(st0, "st_atime"), attr(st1, "st_atime"))
         # Set to the current time in the old explicit way.
-        os.utime(support.TESTFN, None)
-        st1 = os.stat(support.TESTFN)
-        # Set to the current time in the new way
-        os.utime(support.TESTFN)
+        os.utime(filename, None)
         st2 = os.stat(support.TESTFN)
-        self.assertAlmostEqual(st1.st_mtime, st2.st_mtime, delta=10)
+        # Set to the current time in the new way
+        os.utime(filename)
+        st3 = os.stat(filename)
+        self.assertAlmostEqual(attr(st2, "st_mtime"), attr(st3, "st_mtime"), delta=delta)
+
+    def test_utime(self):
+        def utime(file, times):
+            return os.utime(file, times)
+        self._test_utime(self.fname, getattr, utime, 10)
+        self._test_utime(support.TESTFN, getattr, utime, 10)
+
+
+    def _test_utime_ns(self, set_times_ns, test_dir=True):
+        def getattr_ns(o, attr):
+            return getattr(o, attr + "_ns")
+        ten_s = 10 * 1000 * 1000 * 1000
+        self._test_utime(self.fname, getattr_ns, set_times_ns, ten_s)
+        if test_dir:
+            self._test_utime(support.TESTFN, getattr_ns, set_times_ns, ten_s)
+
+    def test_utime_ns(self):
+        def utime_ns(file, times):
+            return os.utime(file, ns=times)
+        self._test_utime_ns(utime_ns)
+
+    requires_lutimes = unittest.skipUnless(hasattr(os, 'lutimes'),
+                            "os.lutimes required for this test.")
+    requires_futimes = unittest.skipUnless(hasattr(os, 'futimes'),
+                            "os.futimes required for this test.")
+
+    @requires_lutimes
+    def test_lutimes_ns(self):
+        def lutimes_ns(file, times):
+            return os.lutimes(file, ns=times)
+        self._test_utime_ns(lutimes_ns)
+
+    @requires_futimes
+    def test_futimes_ns(self):
+        def futimes_ns(file, times):
+            with open(file, "wb") as f:
+                os.futimes(f.fileno(), ns=times)
+        self._test_utime_ns(futimes_ns, test_dir=False)
+
+    def _utime_invalid_arguments(self, name, arg):
+        with self.assertRaises(RuntimeError):
+            getattr(os, name)(arg, (5, 5), ns=(5, 5))
+
+    def test_utime_invalid_arguments(self):
+        self._utime_invalid_arguments('utime', self.fname)
+
+    @requires_lutimes
+    def test_lutimes_invalid_arguments(self):
+        self._utime_invalid_arguments('lutimes', self.fname)
+
+    @requires_futimes
+    def test_futimes_invalid_arguments(self):
+        with open(self.fname, "wb") as f:
+            self._utime_invalid_arguments('futimes', f.fileno())
+
 
     @unittest.skipUnless(stat_supports_subsecond,
                          "os.stat() doesn't has a subsecond resolution")
@@ -338,8 +398,7 @@
             os.utime(filename, (atime, mtime))
         self._test_utime_subsecond(set_time)
 
-    @unittest.skipUnless(hasattr(os, 'futimes'),
-                         "os.futimes required for this test.")
+    @requires_futimes
     def test_futimes_subsecond(self):
         def set_time(filename, atime, mtime):
             with open(filename, "wb") as f:
@@ -375,8 +434,7 @@
                 os.close(dirfd)
         self._test_utime_subsecond(set_time)
 
-    @unittest.skipUnless(hasattr(os, 'lutimes'),
-                         "os.lutimes required for this test.")
+    @requires_lutimes
     def test_lutimes_subsecond(self):
         def set_time(filename, atime, mtime):
             os.lutimes(filename, (atime, mtime))