From 181e30f5f8d93a1673f940e3303fbe0218b6c3df Mon Sep 17 00:00:00 2001 From: va4es2 Date: Sun, 27 Dec 2020 07:42:08 +0300 Subject: [PATCH 1/2] FastZip - fix TimeSetting control when extracting Zip entries --- src/ICSharpCode.SharpZipLib/Zip/FastZip.cs | 89 ++++++++++++++++++- .../Zip/IEntryFactory.cs | 12 +++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs index 348527e4f..507fb2351 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs @@ -3,6 +3,7 @@ using System; using System.IO; using static ICSharpCode.SharpZipLib.Zip.Compression.Deflater; +using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; namespace ICSharpCode.SharpZipLib.Zip { @@ -195,6 +196,26 @@ public FastZip() { } + /// + /// Initialise a new instance of using the specified + /// + /// The time setting to use when creating or extracting Zip entries. + public FastZip(TimeSetting timeSetting) + { + entryFactory_ = new ZipEntryFactory(timeSetting); + restoreDateTimeOnExtract_ = true; + } + + /// + /// Initialise a new instance of using the specified + /// + /// The time to set all values to created or extracted Zip Entries. + public FastZip(DateTime time) + { + entryFactory_ = new ZipEntryFactory(time); + restoreDateTimeOnExtract_ = true; + } + /// /// Initialise a new instance of /// @@ -735,7 +756,39 @@ private void ExtractFileEntry(ZipEntry entry, string targetName) if (restoreDateTimeOnExtract_) { - File.SetLastWriteTime(targetName, entry.DateTime); + switch (entryFactory_.Setting) + { + case TimeSetting.CreateTime: + File.SetCreationTime(targetName, entry.DateTime); + break; + + case TimeSetting.CreateTimeUtc: + File.SetCreationTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.LastAccessTime: + File.SetLastAccessTime(targetName, entry.DateTime); + break; + + case TimeSetting.LastAccessTimeUtc: + File.SetLastAccessTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.LastWriteTime: + File.SetLastWriteTime(targetName, entry.DateTime); + break; + + case TimeSetting.LastWriteTimeUtc: + File.SetLastWriteTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.Fixed: + File.SetLastWriteTime(targetName, entryFactory_.FixedDateTime); + break; + + default: + throw new ZipException("Unhandled time setting in ExtractFileEntry"); + } } if (RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1)) @@ -809,7 +862,39 @@ private void ExtractEntry(ZipEntry entry) Directory.CreateDirectory(dirName); if (entry.IsDirectory && restoreDateTimeOnExtract_) { - Directory.SetLastWriteTime(dirName, entry.DateTime); + switch (entryFactory_.Setting) + { + case TimeSetting.CreateTime: + Directory.SetCreationTime(dirName, entry.DateTime); + break; + + case TimeSetting.CreateTimeUtc: + Directory.SetCreationTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.LastAccessTime: + Directory.SetLastAccessTime(dirName, entry.DateTime); + break; + + case TimeSetting.LastAccessTimeUtc: + Directory.SetLastAccessTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.LastWriteTime: + Directory.SetLastWriteTime(dirName, entry.DateTime); + break; + + case TimeSetting.LastWriteTimeUtc: + Directory.SetLastWriteTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.Fixed: + Directory.SetLastWriteTime(dirName, entryFactory_.FixedDateTime); + break; + + default: + throw new ZipException("Unhandled time setting in ExtractEntry"); + } } } else diff --git a/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs b/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs index bbe40c4d7..1b238f01a 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs @@ -1,4 +1,6 @@ +using System; using ICSharpCode.SharpZipLib.Core; +using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; namespace ICSharpCode.SharpZipLib.Zip { @@ -50,5 +52,15 @@ public interface IEntryFactory /// Get/set the applicable. /// INameTransform NameTransform { get; set; } + + /// + /// Get the in use. + /// + TimeSetting Setting { get; } + + /// + /// Get the value to use when is set to + /// + DateTime FixedDateTime { get; } } } From 3e865dd4d7189fba39c7cca842c474f98125974a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sat, 6 Feb 2021 16:32:47 +0100 Subject: [PATCH 2/2] Add tests for FastZip file times --- src/ICSharpCode.SharpZipLib/Zip/FastZip.cs | 5 +- .../Zip/IEntryFactory.cs | 3 +- .../Zip/ZipEntryFactory.cs | 2 +- .../Zip/FastZipHandling.cs | 175 ++++++++++++++++++ 4 files changed, 182 insertions(+), 3 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs index 507fb2351..01725f4c3 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs @@ -200,6 +200,9 @@ public FastZip() /// Initialise a new instance of using the specified /// /// The time setting to use when creating or extracting Zip entries. + /// Using TimeSetting.LastAccessTime[Utc] when + /// creating an archive will set the file time to the moment of reading. + /// public FastZip(TimeSetting timeSetting) { entryFactory_ = new ZipEntryFactory(timeSetting); @@ -209,7 +212,7 @@ public FastZip(TimeSetting timeSetting) /// /// Initialise a new instance of using the specified /// - /// The time to set all values to created or extracted Zip Entries. + /// The time to set all values for created or extracted Zip Entries. public FastZip(DateTime time) { entryFactory_ = new ZipEntryFactory(time); diff --git a/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs b/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs index 1b238f01a..d7ec18140 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs @@ -59,7 +59,8 @@ public interface IEntryFactory TimeSetting Setting { get; } /// - /// Get the value to use when is set to + /// Get the value to use when is set to , + /// or if not specified, the value of when the class was the initialized /// DateTime FixedDateTime { get; } } diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs index e82eafc48..1e40baaff 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs @@ -364,7 +364,7 @@ public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem) private INameTransform nameTransform_; private DateTime fixedDateTime_ = DateTime.Now; - private TimeSetting timeSetting_; + private TimeSetting timeSetting_ = TimeSetting.LastWriteTime; private bool isUnicodeText_; private int getAttributes_ = -1; diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs index 753fc8623..aa863b352 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; +using TimeSetting = ICSharpCode.SharpZipLib.Zip.ZipEntryFactory.TimeSetting; namespace ICSharpCode.SharpZipLib.Tests.Zip { @@ -686,5 +687,179 @@ public void CreateZipShouldLeaveOutputStreamOpenIfRequested(bool leaveOpen) } } } + + [Category("Zip")] + [Category("CreatesTempFile")] + [Test] + public void CreateZipShouldSetTimeOnEntriesFromConstructorDateTime() + { + var targetTime = TestTargetTime(TimeSetting.Fixed); + var fastZip = new FastZip(targetTime); + var target = CreateFastZipTestArchiveWithAnEntry(fastZip); + var archive = new MemoryStream(target.ToArray()); + using (var zf = new ZipFile(archive)) + { + Assert.AreEqual(targetTime, zf[0].DateTime); + } + } + + [Category("Zip")] + [Category("CreatesTempFile")] + [TestCase(TimeSetting.CreateTimeUtc), TestCase(TimeSetting.LastWriteTimeUtc), TestCase(TimeSetting.LastAccessTimeUtc)] + [TestCase(TimeSetting.CreateTime), TestCase(TimeSetting.LastWriteTime), TestCase(TimeSetting.LastAccessTime)] + public void CreateZipShouldSetTimeOnEntriesFromConstructorTimeSetting(TimeSetting timeSetting) + { + var targetTime = TestTargetTime(timeSetting); + var fastZip = new FastZip(timeSetting); + + var alterTime = (Action) null; + switch(timeSetting) + { + case TimeSetting.LastWriteTime: alterTime = fi => fi.LastWriteTime = targetTime; break; + case TimeSetting.LastWriteTimeUtc: alterTime = fi => fi.LastWriteTimeUtc = targetTime; break; + case TimeSetting.CreateTime: alterTime = fi => fi.CreationTime = targetTime; break; + case TimeSetting.CreateTimeUtc: alterTime = fi => fi.CreationTimeUtc = targetTime; break; + } + + var target = CreateFastZipTestArchiveWithAnEntry(fastZip, alterTime); + // Check that the file contents are correct in both cases + var archive = new MemoryStream(target.ToArray()); + using (var zf = new ZipFile(archive)) + { + Assert.AreEqual(TestTargetTime(timeSetting), zf[0].DateTime); + } + } + + [Category("Zip")] + [Category("CreatesTempFile")] + [TestCase(TimeSetting.CreateTimeUtc), TestCase(TimeSetting.LastWriteTimeUtc), TestCase(TimeSetting.LastAccessTimeUtc)] + [TestCase(TimeSetting.CreateTime), TestCase(TimeSetting.LastWriteTime), TestCase(TimeSetting.LastAccessTime)] + [TestCase(TimeSetting.Fixed)] + public void ExtractZipShouldSetTimeOnFilesFromConstructorTimeSetting(TimeSetting timeSetting) + { + var targetTime = ExpectedFixedTime(); + var archiveStream = CreateFastZipTestArchiveWithAnEntry(new FastZip(targetTime)); + + if (timeSetting == TimeSetting.Fixed) + { + Assert.Ignore("Fixed time without specifying a time is undefined"); + } + + var fastZip = new FastZip(timeSetting); + using (var extractDir = new Utils.TempDir()) + { + fastZip.ExtractZip(archiveStream, extractDir.Fullpath, FastZip.Overwrite.Always, + _ => true, "", "", true, true, false); + var fi = new FileInfo(Path.Combine(extractDir.Fullpath, SingleEntryFileName)); + Assert.AreEqual(targetTime, FileTimeFromTimeSetting(fi, timeSetting)); + } + } + + [Category("Zip")] + [Category("CreatesTempFile")] + [TestCase(DateTimeKind.Local), TestCase(DateTimeKind.Utc)] + public void ExtractZipShouldSetTimeOnFilesFromConstructorDateTime(DateTimeKind dtk) + { + // Create the archive with a fixed "bad" datetime + var target = CreateFastZipTestArchiveWithAnEntry(new FastZip(UnexpectedFixedTime(dtk))); + + // Extract the archive with a fixed time override + var targetTime = ExpectedFixedTime(dtk); + var fastZip = new FastZip(targetTime); + using (var extractDir = new Utils.TempDir()) + { + fastZip.ExtractZip(target, extractDir.Fullpath, FastZip.Overwrite.Always, + _ => true, "", "", true, true, false); + var fi = new FileInfo(Path.Combine(extractDir.Fullpath, SingleEntryFileName)); + var fileTime = FileTimeFromTimeSetting(fi, TimeSetting.Fixed); + if (fileTime.Kind != dtk) fileTime = fileTime.ToUniversalTime(); + Assert.AreEqual(targetTime, fileTime); + } + } + + [Category("Zip")] + [Category("CreatesTempFile")] + [TestCase(DateTimeKind.Local), TestCase(DateTimeKind.Utc)] + public void ExtractZipShouldSetTimeOnFilesWithEmptyConstructor(DateTimeKind dtk) + { + // Create the archive with a fixed datetime + var targetTime = ExpectedFixedTime(dtk); + var target = CreateFastZipTestArchiveWithAnEntry(new FastZip(targetTime)); + + // Extract the archive with an empty constructor + var fastZip = new FastZip(); + using (var extractDir = new Utils.TempDir()) + { + fastZip.ExtractZip(target, extractDir.Fullpath, FastZip.Overwrite.Always, + _ => true, "", "", true, true, false); + var fi = new FileInfo(Path.Combine(extractDir.Fullpath, SingleEntryFileName)); + Assert.AreEqual(targetTime, FileTimeFromTimeSetting(fi, TimeSetting.Fixed)); + } + } + + private static bool IsLastAccessTime(TimeSetting ts) + => ts == TimeSetting.LastAccessTime || ts == TimeSetting.LastAccessTimeUtc; + + private static DateTime FileTimeFromTimeSetting(FileInfo fi, TimeSetting timeSetting) + { + switch (timeSetting) + { + case TimeSetting.LastWriteTime: return fi.LastWriteTime; + case TimeSetting.LastWriteTimeUtc: return fi.LastWriteTimeUtc; + case TimeSetting.CreateTime: return fi.CreationTime; + case TimeSetting.CreateTimeUtc: return fi.CreationTimeUtc; + case TimeSetting.LastAccessTime: return fi.LastAccessTime; + case TimeSetting.LastAccessTimeUtc: return fi.LastAccessTimeUtc; + case TimeSetting.Fixed: return fi.LastWriteTime; + } + + throw new ArgumentException("Invalid TimeSetting", nameof(timeSetting)); + } + + private static DateTime TestTargetTime(TimeSetting ts) + { + var dtk = ts == TimeSetting.CreateTimeUtc + || ts == TimeSetting.LastWriteTimeUtc + || ts == TimeSetting.LastAccessTimeUtc + ? DateTimeKind.Utc + : DateTimeKind.Local; + + return IsLastAccessTime(ts) + // AccessTime will be altered by reading/writing the file entry + ? CurrentTime(dtk) + : ExpectedFixedTime(dtk); + } + + private static DateTime CurrentTime(DateTimeKind kind) + { + var now = kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; + return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, (now.Second / 2) * 2, kind); + } + + private static DateTime ExpectedFixedTime(DateTimeKind dtk = DateTimeKind.Unspecified) + => new DateTime(2010, 5, 30, 16, 22, 50, dtk); + private static DateTime UnexpectedFixedTime(DateTimeKind dtk = DateTimeKind.Unspecified) + => new DateTime(1980, 10, 11, 22, 39, 30, dtk); + + private const string SingleEntryFileName = "testEntry.dat"; + + private static TrackedMemoryStream CreateFastZipTestArchiveWithAnEntry(FastZip fastZip, Action alterFile = null) + { + var target = new TrackedMemoryStream(); + + using (var tempFolder = new Utils.TempDir()) + { + + // Create test input file + var addFile = Path.Combine(tempFolder.Fullpath, SingleEntryFileName); + MakeTempFile(addFile, 16); + var fi = new FileInfo(addFile); + alterFile?.Invoke(fi); + + fastZip.CreateZip(target, tempFolder.Fullpath, false, SingleEntryFileName, null, leaveOpen: true); + } + + return target; + } } }