From 64af3597af4507ffa901fa141b7aa4ddbc4c9edd Mon Sep 17 00:00:00 2001 From: David Cantu Date: Fri, 9 Sep 2022 08:54:20 -0700 Subject: [PATCH 1/7] Fix buffer too small error on prefix --- .../src/System/Formats/Tar/TarHeader.Write.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs index e1166a066814b3..7234482d3d4820 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs @@ -374,12 +374,10 @@ private int WritePosixName(Span buffer) if (_name.Length > FieldLengths.Name) { - int prefixBytesLength = Math.Min(_name.Length - FieldLengths.Name, FieldLengths.Name); - Span remaining = prefixBytesLength <= 256 ? - stackalloc byte[prefixBytesLength] : - new byte[prefixBytesLength]; - - int encoded = Encoding.ASCII.GetBytes(_name.AsSpan(FieldLengths.Name), remaining); + int prefixBytesLength = Math.Min(_name.Length - FieldLengths.Name, FieldLengths.Prefix); + Debug.Assert(prefixBytesLength <= 256); + Span remaining = stackalloc byte[prefixBytesLength]; + int encoded = Encoding.ASCII.GetBytes(_name.AsSpan(FieldLengths.Name, prefixBytesLength), remaining); Debug.Assert(encoded == remaining.Length); checksum += WriteLeftAlignedBytesAndGetChecksum(remaining, buffer.Slice(FieldLocations.Prefix, FieldLengths.Prefix)); From bac54fa1baa6326fff94ed0f2ec1341c08dc09e3 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Fri, 9 Sep 2022 08:54:57 -0700 Subject: [PATCH 2/7] Add test --- .../TarWriter/TarWriter.WriteEntry.Tests.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs index b0c78298f2bb01..4b2f7ac6b46772 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs @@ -299,5 +299,62 @@ public void WriteTimestampsBeyondOctalLimit(TarEntryFormat format) } } } + + [Theory] + [InlineData(TarEntryFormat.V7)] + // On reading, ustar just combines prefix(155) + '/' + name(100), which may be wrong but that's how it currently is. + // On writing, it writes the first 100 chars of entryName on name and the next 155 on prefix, so it is flipping the name+prefix on reading. + //[InlineData(TarEntryFormat.Ustar)] + [InlineData(TarEntryFormat.Pax)] + [InlineData(TarEntryFormat.Gnu)] + public void WriteLongName(TarEntryFormat format) + { + string maxPathComponent = new string('a', 255); + WriteLongNameCore(format, maxPathComponent); + + maxPathComponent = new string('a', 90) + new string('b', 165); + WriteLongNameCore(format, maxPathComponent); + + maxPathComponent = new string('a', 165) + new string('b', 90); + WriteLongNameCore(format, maxPathComponent); + } + + private void WriteLongNameCore(TarEntryFormat format, string maxPathComponent) + { + Assert.Equal(255, maxPathComponent.Length); + + var ms = new MemoryStream(); + using (var writer = new TarWriter(ms, true)) + { + TarEntryType entryType = format == TarEntryFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; + var entry1 = InvokeTarEntryCreationConstructor(format, entryType, maxPathComponent); + writer.WriteEntry(entry1); + + var entry2 = InvokeTarEntryCreationConstructor(format, entryType, Path.Join(maxPathComponent, maxPathComponent)); + writer.WriteEntry(entry2); + } + + ms.Position = 0; + using var reader = new TarReader(ms); + + TarEntry readEntry = reader.GetNextEntry(); + string expectedName = GetExpectedNameForFormat(format, maxPathComponent); + Assert.Equal(expectedName, readEntry.Name); + + readEntry = reader.GetNextEntry(); + expectedName = GetExpectedNameForFormat(format, Path.Join(maxPathComponent, maxPathComponent)); + Assert.Equal(expectedName, readEntry.Name); + + Assert.Null(reader.GetNextEntry()); + + string GetExpectedNameForFormat(TarEntryFormat format, string expectedName) + { + if (format is TarEntryFormat.V7) // V7 truncates names at 100 characters. + { + return expectedName.Substring(0, 100); + } + return expectedName; + } + } } } From 63b91582f541b75e2c5e9dfe6914dde69701aa90 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Fri, 9 Sep 2022 09:03:53 -0700 Subject: [PATCH 3/7] Remove var usage --- .../TarWriter/TarWriter.WriteEntry.Tests.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs index 4b2f7ac6b46772..d7ffed3da87c99 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs @@ -323,27 +323,28 @@ private void WriteLongNameCore(TarEntryFormat format, string maxPathComponent) { Assert.Equal(255, maxPathComponent.Length); - var ms = new MemoryStream(); - using (var writer = new TarWriter(ms, true)) + TarEntry entry; + MemoryStream ms = new(); + using (TarWriter writer = new(ms, true)) { TarEntryType entryType = format == TarEntryFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; - var entry1 = InvokeTarEntryCreationConstructor(format, entryType, maxPathComponent); - writer.WriteEntry(entry1); + entry = InvokeTarEntryCreationConstructor(format, entryType, maxPathComponent); + writer.WriteEntry(entry); - var entry2 = InvokeTarEntryCreationConstructor(format, entryType, Path.Join(maxPathComponent, maxPathComponent)); - writer.WriteEntry(entry2); + entry = InvokeTarEntryCreationConstructor(format, entryType, Path.Join(maxPathComponent, maxPathComponent)); + writer.WriteEntry(entry); } ms.Position = 0; - using var reader = new TarReader(ms); + using TarReader reader = new(ms); - TarEntry readEntry = reader.GetNextEntry(); + entry = reader.GetNextEntry(); string expectedName = GetExpectedNameForFormat(format, maxPathComponent); - Assert.Equal(expectedName, readEntry.Name); + Assert.Equal(expectedName, entry.Name); - readEntry = reader.GetNextEntry(); + entry = reader.GetNextEntry(); expectedName = GetExpectedNameForFormat(format, Path.Join(maxPathComponent, maxPathComponent)); - Assert.Equal(expectedName, readEntry.Name); + Assert.Equal(expectedName, entry.Name); Assert.Null(reader.GetNextEntry()); From 7c407ee3229d43903b3d47226c197a488d12b2f9 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Fri, 9 Sep 2022 09:16:14 -0700 Subject: [PATCH 4/7] Remove assert --- .../System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs index 7234482d3d4820..43fe79ce7dc0a6 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHeader.Write.cs @@ -375,7 +375,6 @@ private int WritePosixName(Span buffer) if (_name.Length > FieldLengths.Name) { int prefixBytesLength = Math.Min(_name.Length - FieldLengths.Name, FieldLengths.Prefix); - Debug.Assert(prefixBytesLength <= 256); Span remaining = stackalloc byte[prefixBytesLength]; int encoded = Encoding.ASCII.GetBytes(_name.AsSpan(FieldLengths.Name, prefixBytesLength), remaining); Debug.Assert(encoded == remaining.Length); From 04d14e5ac67bb2511e79606801e0d221df1a9b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Fri, 9 Sep 2022 11:24:05 -0700 Subject: [PATCH 5/7] Update src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs --- .../tests/TarWriter/TarWriter.WriteEntry.Tests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs index d7ffed3da87c99..9c2aa572c344c4 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs @@ -302,9 +302,7 @@ public void WriteTimestampsBeyondOctalLimit(TarEntryFormat format) [Theory] [InlineData(TarEntryFormat.V7)] - // On reading, ustar just combines prefix(155) + '/' + name(100), which may be wrong but that's how it currently is. - // On writing, it writes the first 100 chars of entryName on name and the next 155 on prefix, so it is flipping the name+prefix on reading. - //[InlineData(TarEntryFormat.Ustar)] + // [InlineData(TarEntryFormat.Ustar)] https://github.com/dotnet/runtime/issues/75360 [InlineData(TarEntryFormat.Pax)] [InlineData(TarEntryFormat.Gnu)] public void WriteLongName(TarEntryFormat format) From a2d1aa5c1ba95edec2f6f0fa5caa5e5836451f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Mon, 12 Sep 2022 08:27:04 -0700 Subject: [PATCH 6/7] Add more lengths and improve test --- .../TarWriter/TarWriter.WriteEntry.Tests.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs index 9c2aa572c344c4..7d1133e6eb7a5c 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs @@ -307,20 +307,16 @@ public void WriteTimestampsBeyondOctalLimit(TarEntryFormat format) [InlineData(TarEntryFormat.Gnu)] public void WriteLongName(TarEntryFormat format) { - string maxPathComponent = new string('a', 255); - WriteLongNameCore(format, maxPathComponent); - - maxPathComponent = new string('a', 90) + new string('b', 165); - WriteLongNameCore(format, maxPathComponent); - - maxPathComponent = new string('a', 165) + new string('b', 90); - WriteLongNameCore(format, maxPathComponent); + var r = new Random(); + foreach (int length in new[] { 99, 100, 101, 199, 200, 201, 254, 255, 256 }) + { + string name = string.Concat(Enumerable.Range(0, length).Select(_ => (char)('a' + r.Next(26)))); + WriteLongNameCore(format, name); + } } private void WriteLongNameCore(TarEntryFormat format, string maxPathComponent) { - Assert.Equal(255, maxPathComponent.Length); - TarEntry entry; MemoryStream ms = new(); using (TarWriter writer = new(ms, true)) @@ -348,7 +344,7 @@ private void WriteLongNameCore(TarEntryFormat format, string maxPathComponent) string GetExpectedNameForFormat(TarEntryFormat format, string expectedName) { - if (format is TarEntryFormat.V7) // V7 truncates names at 100 characters. + if (format is TarEntryFormat.V7 && expectedName.Length > 100) // V7 truncates names at 100 characters. { return expectedName.Substring(0, 100); } From fe1d82e5262d5e9a7075ed59d0b0c1f3c7f12fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Mon, 12 Sep 2022 09:08:19 -0700 Subject: [PATCH 7/7] Add missing using --- .../tests/TarWriter/TarWriter.WriteEntry.Tests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs index 7d1133e6eb7a5c..2808914db4e52a 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.WriteEntry.Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using Xunit; namespace System.Formats.Tar.Tests