From 26e0b1deec095635f6a160eb7875c7fb48d53f5a Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 23 Jan 2019 07:05:59 -0500 Subject: [PATCH] =?utf8?q?Fixes=20#12479=20-=20Fixed=20issue=20where=20tim?= =?utf8?q?e=20lookups=20on=20a=20symlink=20(GetLastWrite,=20Creation,=20?= =?utf8?q?=E2=80=A6=20(#12533)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When given a path to a symlink `GetLastWriteTime` returned the time for the actual file. This PR resolves that. Fixes https://github.com/mono/mono/issues/12479 --- mcs/class/corlib/Test/System.IO/FileTest.cs | 56 +++++++++++++++++++++++++++++ mono/metadata/w32file-unix.c | 26 +++++++------- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/mcs/class/corlib/Test/System.IO/FileTest.cs b/mcs/class/corlib/Test/System.IO/FileTest.cs index fff4be0e3a5..4020a5e3a3e 100644 --- a/mcs/class/corlib/Test/System.IO/FileTest.cs +++ b/mcs/class/corlib/Test/System.IO/FileTest.cs @@ -2779,6 +2779,62 @@ namespace MonoTests.System.IO public static extern int symlink (string oldpath, string newpath); [Test] + [Category ("NotWasm")] + public void SymLinkStats() { + if (!RunningOnUnix) + Assert.Ignore ("Symlink are hard on windows"); + + var name1 = Path.GetRandomFileName (); + var name2 = Path.GetRandomFileName (); + + var path1 = Path.Combine (Path.GetTempPath (), name1); + var path2 = Path.Combine (Path.GetTempPath (), name2); + + File.Delete (path1); + File.Delete (path2); + + try { + using (var f = File.Create(path1)) { + Assert.IsNotNull (f, "Path1 must be created for symlink stats"); + } + + if (symlink (path1, path2) != 0) + Assert.Fail ("symlink #1 failed with errno={0}", Marshal.GetLastWin32Error ()); + + Assert.IsTrue (File.Exists (path1), "File.Exists must return true for path1 symlink stats"); + Assert.IsTrue (File.Exists (path2), "File.Exists must return true for path2 symlink stats"); + + try { + File.SetLastWriteTime (path1, DateTime.Now.AddMinutes(-5)); + File.SetCreationTime (path1, DateTime.Now.AddMinutes(-5)); + File.SetLastAccessTime (path1, DateTime.Now.AddMinutes(-5)); + + Assert.AreNotEqual (File.GetLastWriteTime(path1), File.GetLastWriteTime (path2), "Path1 and Path2 should not have the same last write times"); + Assert.AreNotEqual (File.GetCreationTime(path1), File.GetCreationTime (path2), "Path1 and Path2 should not have the same creation times"); + Assert.AreNotEqual (File.GetLastAccessTime(path1), File.GetLastAccessTime (path2), "Path1 and Path2 should not have the same last access times"); + } + catch(IOException e) { + Assert.Fail ("Symlink stats should allow setting/getting time on file"); + } + + File.Delete (path2); + Assert.IsTrue (File.Exists(path1), "Original file must still exist for symlink stats"); + Assert.IsFalse (File.Exists(path2), "File.Delete must delete symlink only for symlink stats"); + + File.Delete (path1); + Assert.IsFalse (File.Exists (path1), "File.Delete must delete symlink stats"); + + } finally { + try { + File.Delete (path1); + File.Delete (path2); + } catch (IOException) { + //Don't double fault any exception from the tests. + } + } + } + + [Test] #if MONOTOUCH_TV [Ignore ("See bug #59239")] #endif diff --git a/mono/metadata/w32file-unix.c b/mono/metadata/w32file-unix.c index e84624d4056..ebb44e6aaef 100644 --- a/mono/metadata/w32file-unix.c +++ b/mono/metadata/w32file-unix.c @@ -3579,25 +3579,25 @@ mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size; #if HAVE_STRUCT_STAT_ST_ATIMESPEC - if (buf.st_mtimespec.tv_sec < buf.st_ctimespec.tv_sec || (buf.st_mtimespec.tv_sec == buf.st_ctimespec.tv_sec && buf.st_mtimespec.tv_nsec < buf.st_ctimespec.tv_nsec)) - stat->creation_time = buf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (buf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + if (linkbuf.st_mtimespec.tv_sec < linkbuf.st_ctimespec.tv_sec || (linkbuf.st_mtimespec.tv_sec == linkbuf.st_ctimespec.tv_sec && linkbuf.st_mtimespec.tv_nsec < linkbuf.st_ctimespec.tv_nsec)) + stat->creation_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; else - stat->creation_time = buf.st_ctimespec.tv_sec * TICKS_PER_SECOND + (buf.st_ctimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->creation_time = linkbuf.st_ctimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_ctimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; - stat->last_access_time = buf.st_atimespec.tv_sec * TICKS_PER_SECOND + (buf.st_atimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; - stat->last_write_time = buf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (buf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->last_access_time = linkbuf.st_atimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_atimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->last_write_time = linkbuf.st_mtimespec.tv_sec * TICKS_PER_SECOND + (linkbuf.st_mtimespec.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; #elif HAVE_STRUCT_STAT_ST_ATIM - if (buf.st_mtime < buf.st_ctime || (buf.st_mtime == buf.st_ctime && buf.st_mtim.tv_nsec < buf.st_ctim.tv_nsec)) - stat->creation_time = buf.st_mtime * TICKS_PER_SECOND + (buf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + if (linkbuf.st_mtime < linkbuf.st_ctime || (linkbuf.st_mtime == linkbuf.st_ctime && linkbuf.st_mtim.tv_nsec < linkbuf.st_ctim.tv_nsec)) + stat->creation_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; else - stat->creation_time = buf.st_ctime * TICKS_PER_SECOND + (buf.st_ctim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->creation_time = linkbuf.st_ctime * TICKS_PER_SECOND + (linkbuf.st_ctim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; - stat->last_access_time = buf.st_atime * TICKS_PER_SECOND + (buf.st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; - stat->last_write_time = buf.st_mtime * TICKS_PER_SECOND + (buf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->last_access_time = linkbuf.st_atime * TICKS_PER_SECOND + (linkbuf.st_atim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; + stat->last_write_time = linkbuf.st_mtime * TICKS_PER_SECOND + (linkbuf.st_mtim.tv_nsec / NANOSECONDS_PER_MICROSECOND) * TICKS_PER_MICROSECOND + CONVERT_BASE; #else - stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * TICKS_PER_SECOND) + CONVERT_BASE; - stat->last_access_time = (((guint64) (buf.st_atime)) * TICKS_PER_SECOND) + CONVERT_BASE; - stat->last_write_time = (((guint64) (buf.st_mtime)) * TICKS_PER_SECOND) + CONVERT_BASE; + stat->creation_time = (((guint64) (linkbuf.st_mtime < linkbuf.st_ctime ? linkbuf.st_mtime : linkbuf.st_ctime)) * TICKS_PER_SECOND) + CONVERT_BASE; + stat->last_access_time = (((guint64) (linkbuf.st_atime)) * TICKS_PER_SECOND) + CONVERT_BASE; + stat->last_write_time = (((guint64) (linkbuf.st_mtime)) * TICKS_PER_SECOND) + CONVERT_BASE; #endif g_free (utf8_name); -- 2.11.4.GIT