Using Utimes instead of utime hence setting millisecond attribute of last access...
authorAnirudh Agnihotry <anirudhagnihotry098@gmail.com>
Thu, 2 Aug 2018 21:10:30 +0000 (14:10 -0700)
committerGitHub <noreply@github.com>
Thu, 2 Aug 2018 21:10:30 +0000 (14:10 -0700)
* using utimes instead of utime

* Test for checking millisec attribute is set or not

* Documenting the values in case of the failure

* NanoSecond enabled

* feedback addressed

* int64_t added

* Fixing build on ci Osx(on which utimensat is not available)

* Minor nits done

Commit migrated from https://github.com/dotnet/corefx/commit/cd62bb271e98410319460b4ba48332416bcccf65

src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTimensat.cs [moved from src/libraries/Common/src/Interop/Unix/System.Native/Interop.UTime.cs with 78% similarity]
src/libraries/Native/Unix/Common/pal_config.h.in
src/libraries/Native/Unix/System.Native/pal_time.c
src/libraries/Native/Unix/System.Native/pal_time.h
src/libraries/Native/Unix/configure.cmake
src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj
src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs
src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs

@@ -9,10 +9,10 @@ internal static partial class Interop
 {
     internal static partial class Sys
     {
-        internal struct UTimBuf
+        internal struct TimeSpec
         {
-            internal long AcTime;
-            internal long ModTime;
+            internal long TvSec;
+            internal long TvNsec;
         }
 
         /// <summary>
@@ -23,7 +23,7 @@ internal static partial class Interop
         /// <returns>
         /// Returns 0 on success; otherwise, returns -1 
         /// </returns>
-        [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_UTime", SetLastError = true)]
-        internal static extern int UTime(string path, ref UTimBuf time);
+        [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_UTimensat", SetLastError = true)]
+        internal static extern int UTimensat(string path, ref TimeSpec times);
     }
 }
index be723d3..a0bc6bc 100644 (file)
@@ -77,6 +77,7 @@
 #cmakedefine01 HAVE_CRT_EXTERNS_H
 #cmakedefine01 HAVE_GETDOMAINNAME
 #cmakedefine01 HAVE_UNAME
+#cmakedefine01 HAVE_UTIMENSAT
 #cmakedefine01 HAVE_FUTIMES
 #cmakedefine01 HAVE_FUTIMENS
 #cmakedefine01 HAVE_MKSTEMPS
index 60f702a..b59b0c0 100644 (file)
@@ -9,6 +9,8 @@
 #include <assert.h>
 #include <utime.h>
 #include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <sys/time.h>
 #if HAVE_MACH_ABSOLUTE_TIME
 #include <mach/mach_time.h>
@@ -20,21 +22,27 @@ enum
     SecondsToNanoSeconds = 1000000000 // 10^9
 };
 
-static void ConvertUTimBuf(const UTimBuf* pal, struct utimbuf* native)
+int32_t SystemNative_UTimensat(const char* path, TimeSpec* times)
 {
-    native->actime = (time_t)(pal->AcTime);
-    native->modtime = (time_t)(pal->ModTime);
-}
-
-int32_t SystemNative_UTime(const char* path, UTimBuf* times)
-{
-    assert(times != NULL);
-
-    struct utimbuf temp;
-    ConvertUTimBuf(times, &temp);
-
     int32_t result;
-    while (CheckInterrupted(result = utime(path, &temp)));
+#if HAVE_UTIMENSAT
+    struct timespec origTimes[2];
+    origTimes[0].tv_sec = (time_t)times[0].tv_sec;
+    origTimes[0].tv_nsec = (long)times[0].tv_nsec;
+
+    origTimes[1].tv_sec = (time_t)times[1].tv_sec;
+    origTimes[1].tv_nsec = (long)times[1].tv_nsec;    
+    while (CheckInterrupted(result = utimensat(AT_FDCWD, path, origTimes, 0)));
+#else
+    struct timeval origTimes[2];
+    origTimes[0].tv_sec = (long)times[0].tv_sec;
+    origTimes[0].tv_usec = (int)times[0].tv_nsec / 1000;
+    
+    origTimes[1].tv_sec = (long)times[1].tv_sec;
+    origTimes[1].tv_usec = (int)times[1].tv_nsec / 1000;
+    while (CheckInterrupted(result = utimes(path, origTimes)));
+#endif
+
     return result;
 }
 
index c1a539e..a0deba8 100644 (file)
@@ -7,18 +7,18 @@
 #include "pal_compiler.h"
 #include "pal_types.h"
 
-typedef struct UTimBuf
+typedef struct TimeSpec
 {
-    int64_t AcTime;
-    int64_t ModTime;
-} UTimBuf;
+    int64_t tv_sec; // seconds
+    int64_t tv_nsec; // nanoseconds
+} TimeSpec;
 
 /**
  * Sets the last access and last modified time of a file
  *
  * Returns 0 on success; otherwise, returns -1 and errno is set.
  */
-DLLEXPORT int32_t SystemNative_UTime(const char* path, UTimBuf* time);
+DLLEXPORT int32_t SystemNative_UTimensat(const char* path, TimeSpec* times);
 
 /**
  * Gets the resolution of the timestamp, in counts per second.
index e773681..b533f6a 100644 (file)
@@ -403,6 +403,10 @@ check_function_exists(
     futimens
     HAVE_FUTIMENS)
 
+check_function_exists(
+    utimensat
+    HAVE_UTIMENSAT)
+
 set (PREVIOUS_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
 set (CMAKE_REQUIRED_FLAGS "-Werror -Wsign-conversion")
 
index f7ee1c0..d3c34c6 100644 (file)
     <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.RmDir.cs">
       <Link>Common\Interop\Unix\Interop.RmDir.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.UTime.cs">
-      <Link>Common\Interop\Unix\Interop.UTime.cs</Link>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.UTimensat.cs">
+      <Link>Common\Interop\Unix\Interop.UTimensat.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.Unix.cs">
       <Link>Common\CoreLib\System\IO\PathInternal.Unix.cs</Link>
index 1c9337b..cd769f5 100644 (file)
@@ -2,6 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Runtime.InteropServices;
+
 namespace System.IO
 {
     internal struct FileStatus
@@ -199,11 +201,20 @@ namespace System.IO
             // force a refresh so that we have an up-to-date times for values not being overwritten
             _fileStatusInitialized = -1;
             EnsureStatInitialized(path);
-            Interop.Sys.UTimBuf buf;
-            // we use utime() not utimensat() so we drop the subsecond part
-            buf.AcTime = accessTime ?? _fileStatus.ATime;
-            buf.ModTime = writeTime ?? _fileStatus.MTime;
-            Interop.CheckIo(Interop.Sys.UTime(path, ref buf), path, InitiallyDirectory);
+
+            // we use utimes()/utimensat() to set the accessTime and writeTime
+            Span<Interop.Sys.TimeSpec> buf = stackalloc Interop.Sys.TimeSpec[2];
+
+            // setting second part
+            buf[0].TvSec = accessTime ?? _fileStatus.ATime;
+            buf[1].TvSec = writeTime ?? _fileStatus.MTime;
+
+            // setting nanosecond part
+            buf[0].TvNsec = accessTime == null ? _fileStatus.ATimeNsec : 0;
+            buf[1].TvNsec = writeTime == null ? _fileStatus.MTimeNsec : 0;
+
+            Interop.CheckIo(Interop.Sys.UTimensat(path, ref MemoryMarshal.GetReference(buf)), path, InitiallyDirectory);          
+
             _fileStatusInitialized = -1;
         }
 
index fe770a7..84424b1 100644 (file)
@@ -77,7 +77,7 @@ namespace System.IO.Tests
         }
 
         [ConditionalFact(nameof(isNotHFS))] // OSX HFS driver format does not support millisec granularity
-        public void TimesIncludeMillisecondPart_Unix()
+        public void TimesIncludeMillisecondPart()
         {
             T item = GetExistingItem();
             Assert.All(TimeFunctions(), (function) =>
@@ -109,7 +109,7 @@ namespace System.IO.Tests
         }
 
         [ConditionalFact(nameof(isHFS))]
-        public void TimesIncludeMillisecondPart_OSX()
+        public void TimesIncludeMillisecondPart_HFS()
         {
             T item = GetExistingItem();
             // OSX HFS driver format does not support millisec granularity
index 0aa253b..a0bae54 100644 (file)
@@ -92,5 +92,20 @@ namespace System.IO.Tests
                 });
             }
         }
+
+        [Fact]
+        public void SetLastWriteTimeMilliSec()
+        {
+            string firstFile = GetTestFilePath();
+            string secondFile = GetTestFilePath();
+
+            File.WriteAllText(firstFile, "");
+            File.WriteAllText(secondFile, "");
+
+            File.SetLastAccessTimeUtc(secondFile, DateTime.UtcNow);
+            long firstFileTicks = File.GetLastWriteTimeUtc(firstFile).Ticks;
+            long secondFileTicks = File.GetLastWriteTimeUtc(secondFile).Ticks;
+            Assert.True(firstFileTicks <= secondFileTicks, $"First File Ticks\t{firstFileTicks}\nSecond File Ticks\t{secondFileTicks}");
+        }
     }
 }