diff --git a/src/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs b/src/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs index c398be0d3..be5ac67de 100644 --- a/src/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs @@ -390,10 +390,21 @@ public TarEntry GetNextEntry() byte[] headerBuf = tarBuffer.ReadBlock(); - if (headerBuf == null) { + if (headerBuf == null) + { + hasHitEOF = true; + } + else if (TarBuffer.IsEndOfArchiveBlock(headerBuf)) + { hasHitEOF = true; - } else - hasHitEOF |= TarBuffer.IsEndOfArchiveBlock(headerBuf); + + // Read the second zero-filled block + tarBuffer.ReadBlock(); + } + else + { + hasHitEOF = false; + } if (hasHitEOF) { currentEntry = null; diff --git a/test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs index 5f4b2a7e5..159bb32a9 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs @@ -613,6 +613,47 @@ public void InputStreamOwnership() Assert.IsFalse(memStream.IsDisposed, "Should not be disposed after parent owner close"); } + [Test] + [Category("Tar")] + public void EndBlockHandling() + { + int dummySize = 70145; + + long outCount, inCount; + + using (var ms = new MemoryStream()) + { + using (var tarOut = TarArchive.CreateOutputTarArchive(ms)) + using (var dummyFile = Utils.GetDummyFile(dummySize)) + { + tarOut.IsStreamOwner = false; + tarOut.WriteEntry(TarEntry.CreateEntryFromFile(dummyFile.Filename), false); + } + + outCount = ms.Position; + ms.Seek(0, SeekOrigin.Begin); + + using (var tarIn = TarArchive.CreateInputTarArchive(ms)) + using (var tempDir = new Utils.TempDir()) + { + tarIn.IsStreamOwner = false; + tarIn.ExtractContents(tempDir.Fullpath); + + foreach (var file in Directory.GetFiles(tempDir.Fullpath, "*", SearchOption.AllDirectories)) + { + Console.WriteLine($"Extracted \"{file}\""); + } + } + + inCount = ms.Position; + + Console.WriteLine($"Output count: {outCount}"); + Console.WriteLine($"Input count: {inCount}"); + + Assert.AreEqual(inCount, outCount, "Bytes read and bytes written should be equal"); + } + } + [Test] [Category("Tar")] [Category("Performance")] diff --git a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs index c8cb5d28d..f7a88c90e 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using NUnit.Framework; namespace ICSharpCode.SharpZipLib.Tests.TestSupport @@ -8,6 +9,7 @@ namespace ICSharpCode.SharpZipLib.Tests.TestSupport /// public static class Utils { + static Random random = new Random(); static void Compare(byte[] a, byte[] b) { @@ -25,5 +27,97 @@ static void Compare(byte[] a, byte[] b) } } + public static TempFile GetDummyFile(int size = -1) + { + var tempFile = new TempFile(); + if (size < 0) + { + File.WriteAllText(tempFile.Filename, DateTime.UtcNow.Ticks.ToString("x16")); + } + else if (size > 0) + { + var bytes = Array.CreateInstance(typeof(byte), size) as byte[]; + random.NextBytes(bytes); + File.WriteAllBytes(tempFile.Filename, bytes); + } + return tempFile; + } + + public class TempFile : IDisposable + { + public string Filename { get; internal set; } + + public TempFile() + { + Filename = Path.GetTempFileName(); + } + + #region IDisposable Support + private bool disposed = false; // To detect redundant calls + + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing && File.Exists(Filename)) + { + try + { + File.Delete(Filename); + } + catch { } + } + + disposed = true; + } + } + + public void Dispose() + => Dispose(true); + + #endregion + + } + + public class TempDir : IDisposable + { + public string Fullpath { get; internal set; } + + public TempDir() + { + Fullpath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(Fullpath); + } + + #region IDisposable Support + private bool disposed = false; // To detect redundant calls + + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing && Directory.Exists(Fullpath)) + { + try + { + Directory.Delete(Fullpath, true); + } + catch { } + } + + disposed = true; + } + } + + public void Dispose() + => Dispose(true); + + #endregion + + } } + + }