From c04c2b7223f2c61c30df3a94dc3a080ecc35c6ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:08:37 -0600 Subject: [PATCH] [release/9.0] [Tar] Fill in the file size even if the file is empty. (#107633) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Tar] Fill in the file size even if the file is empty. This matches standard behavior. * fix tests, set size only when datastream is not null * Add unit tests for size header of an empty file * Scope disposables Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> * Update test to verify entry can be read after confirming the sequence is correct. Add test to verify that entries created with the malformed size field are still readable after the bug fix. --------- Co-authored-by: Szymon Sobik Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> --- .../src/System/Formats/Tar/TarHeader.Write.cs | 2 +- .../tests/TarWriter/TarWriter.Tests.cs | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 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 81d90e1d7be21..fcc5d22cde60e 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 @@ -629,7 +629,7 @@ private int WriteCommonFields(Span buffer, TarEntryType actualEntryType) checksum += FormatNumeric(_gid, buffer.Slice(FieldLocations.Gid, FieldLengths.Gid)); } - if (_size > 0) + if (_dataStream != null && _size >= 0) { checksum += FormatNumeric(_size, buffer.Slice(FieldLocations.Size, FieldLengths.Size)); } diff --git a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.Tests.cs index b46816844b44c..26b768bfe33b8 100644 --- a/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarWriter/TarWriter.Tests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; +using System.Linq; using Xunit; namespace System.Formats.Tar.Tests @@ -144,6 +145,74 @@ public void Verify_Checksum_SymbolicLink_LongLink(TarEntryFormat format) => public void Verify_Checksum_SymbolicLink_LongLink_LongPath(TarEntryFormat format) => Verify_Checksum_Internal(format, TarEntryType.SymbolicLink, longPath: true, longLink: true); + [Fact] + public void Verify_Size_RegularFile_Empty() + { + using MemoryStream archiveStream = new(); + string entryName = "entry.txt"; + using (TarWriter archive = new(archiveStream, TarEntryFormat.V7, leaveOpen: true)) + { + V7TarEntry e = new(TarEntryType.V7RegularFile, entryName) + { + DataStream = new MemoryStream(0) + }; + archive.WriteEntry(e); + } + + int sizeLocation = 100 + // Name + 8 + // Mode + 8 + // Uid + 8; // Gid + int sizeLength = 12; + + archiveStream.Position = 0; + byte[] actual = archiveStream.GetBuffer()[sizeLocation..(sizeLocation + sizeLength)]; + + byte[] expected = [0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0]; + AssertExtensions.SequenceEqual(expected, actual); + + archiveStream.Position = 0; + using TarReader reader = new(archiveStream); + + TarEntry? actualEntry = reader.GetNextEntry(); + Assert.NotNull(actualEntry); + Assert.Equal(0, actualEntry.Length); + Assert.Null(actualEntry.DataStream); // No stream created when size field's value is 0 + } + + [Fact] + public void Verify_Compatibility_RegularFile_EmptyFile_NoSizeStored() + { + using MemoryStream archiveStream = new(); + string entryName = "entry.txt"; + using (TarWriter archive = new(archiveStream, TarEntryFormat.V7, leaveOpen: true)) + { + V7TarEntry e = new(TarEntryType.V7RegularFile, entryName) + { + DataStream = new MemoryStream(0) + }; + archive.WriteEntry(e); + } + + int sizeLocation = 100 + // Name + 8 + // Mode + 8 + // Uid + 8; // Gid + + // Fill the size field with 12 zeros as we used to before the bug fix + byte[] replacement = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + archiveStream.Seek(sizeLocation, SeekOrigin.Begin); + archiveStream.Write(replacement); + + archiveStream.Position = 0; + using TarReader reader = new(archiveStream); + + TarEntry? actualEntry = reader.GetNextEntry(); // Should succeed to read the entry with a malformed size field value + Assert.NotNull(actualEntry); + Assert.Equal(0, actualEntry.Length); // Should succeed to detect the size field's value as zero + Assert.Null(actualEntry.DataStream); // No stream created when size field's value is 0 + } + private void Verify_Checksum_Internal(TarEntryFormat format, TarEntryType entryType, bool longPath, bool longLink) { using MemoryStream archive = new MemoryStream();