From e83738e39c93c4ee9af410d4a6e33829289dba50 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Fri, 31 Jul 2026 22:38:34 +0200 Subject: [PATCH] Deduplicate Tar writer/entry helpers - TarWriter.Unix.cs / TarWriter.Windows.cs: extract the duplicated Format switch expression that constructs a new TarEntry into a shared CreateEntryForFormat helper in TarWriter.cs. - TarEntry.Unix.cs / TarEntry.Windows.cs: move the identical ExtractAsHardLink implementation (both just call the cross-platform File.CreateHardLink) into the shared TarEntry.cs. - TarHelpers.cs: simplify GetCorrectTypeFlagForFormat from nested if/else to a tuple pattern-matching switch expression, same behavior. No behavior change. All 6580 System.Formats.Tar.Tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7572f801-427a-403d-aac4-e8c856f1ba08 --- .../src/System/Formats/Tar/TarEntry.Unix.cs | 9 -------- .../System/Formats/Tar/TarEntry.Windows.cs | 9 -------- .../src/System/Formats/Tar/TarEntry.cs | 10 +++++++++ .../src/System/Formats/Tar/TarHelpers.cs | 21 ++++++------------- .../src/System/Formats/Tar/TarWriter.Unix.cs | 9 +------- .../System/Formats/Tar/TarWriter.Windows.cs | 9 +------- .../src/System/Formats/Tar/TarWriter.cs | 11 ++++++++++ 7 files changed, 29 insertions(+), 49 deletions(-) diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Unix.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Unix.cs index 4d8b1f34aaed0b..e224e5ed28c5f1 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Unix.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Unix.cs @@ -31,15 +31,6 @@ private void ExtractAsFifo(string destinationFileName) Interop.CheckIo(Interop.Sys.MkFifo(destinationFileName, (uint)Mode), destinationFileName); } - // Unix specific implementation of the method that extracts the current entry as a hard link. - private void ExtractAsHardLink(string targetFilePath, string hardLinkFilePath) - { - Debug.Assert(EntryType is TarEntryType.HardLink); - Debug.Assert(!string.IsNullOrEmpty(targetFilePath)); - Debug.Assert(!string.IsNullOrEmpty(hardLinkFilePath)); - File.CreateHardLink(hardLinkFilePath, targetFilePath); - } - // On Unix-like systems no explicit step is needed to make a file sparse: the kernel // creates a hole whenever a write is preceded by a seek past the previous end. Most // modern file systems (ext4, btrfs, xfs, APFS, ...) support sparse files; on those that diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Windows.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Windows.cs index c48f20834ead09..4534f2cf792927 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Windows.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.Windows.cs @@ -32,15 +32,6 @@ private void ExtractAsFifo(string destinationFileName) throw new InvalidOperationException(SR.IO_FifoFiles_NotSupported); } - // Windows specific implementation of the method that extracts the current entry as a hard link. - private void ExtractAsHardLink(string targetFilePath, string hardLinkFilePath) - { - Debug.Assert(EntryType is TarEntryType.HardLink); - Debug.Assert(!string.IsNullOrEmpty(targetFilePath)); - Debug.Assert(!string.IsNullOrEmpty(hardLinkFilePath)); - File.CreateHardLink(hardLinkFilePath, targetFilePath); - } - // Best-effort attempt to mark the file as sparse on Windows so subsequent unwritten ranges // remain real holes (unallocated extents) rather than being zero-filled on disk. The call // is silently ignored if the underlying file system does not support sparse files diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs index 71035c6a2d440e..560790ce46a4b6 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs @@ -662,6 +662,16 @@ private static void VerifyDestinationPath(string filePath, bool overwrite) File.Delete(filePath); } + // Extracts the current entry as a hard link. Shared between Unix and Windows since + // File.CreateHardLink is itself cross-platform. + private void ExtractAsHardLink(string targetFilePath, string hardLinkFilePath) + { + Debug.Assert(EntryType is TarEntryType.HardLink); + Debug.Assert(!string.IsNullOrEmpty(targetFilePath)); + Debug.Assert(!string.IsNullOrEmpty(hardLinkFilePath)); + File.CreateHardLink(hardLinkFilePath, targetFilePath); + } + // Extracts the current entry as a regular file into the specified destination. // The assumption is that at this point there is no preexisting file or directory in that destination. private void ExtractAsRegularFile(string destinationFileName) diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs index 89252d198d31dc..e3935204ca8eef 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.cs @@ -180,22 +180,13 @@ internal static bool TryGetStringAsBaseTenLong(IReadOnlyDictionary + (format, entryType) switch { - return TarEntryType.RegularFile; - } - - return entryType; - } + (TarEntryFormat.V7, TarEntryType.RegularFile) => TarEntryType.V7RegularFile, + (not TarEntryFormat.V7, TarEntryType.V7RegularFile) => TarEntryType.RegularFile, + _ => entryType, + }; /// Parses a numeric field. internal static T ParseNumeric(ReadOnlySpan buffer) where T : struct, INumber, IBinaryInteger diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs index 649403e07f0444..864fbb717fac83 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Unix.cs @@ -62,14 +62,7 @@ private TarEntry ConstructEntryForWriting(string fullPath, string entryName, Fil FileSystemInfo info = entryType is TarEntryType.Directory ? new DirectoryInfo(fullPath) : new FileInfo(fullPath); - TarEntry entry = Format switch - { - TarEntryFormat.V7 => new V7TarEntry(entryType, entryName), - TarEntryFormat.Ustar => new UstarTarEntry(entryType, entryName), - TarEntryFormat.Pax => new PaxTarEntry(entryType, entryName), - TarEntryFormat.Gnu => new GnuTarEntry(entryType, entryName), - _ => throw new InvalidDataException(SR.Format(SR.TarInvalidFormat, Format)), - }; + TarEntry entry = CreateEntryForFormat(entryType, entryName); if (entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice) { diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs index b3aa1ba01a235f..340ca4560f761a 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.Windows.cs @@ -107,14 +107,7 @@ private TarEntry ConstructEntryForWriting(string fullPath, string entryName, Fil throw new IOException(SR.Format(SR.TarUnsupportedFile, fullPath)); } - TarEntry entry = Format switch - { - TarEntryFormat.V7 => new V7TarEntry(entryType, entryName), - TarEntryFormat.Ustar => new UstarTarEntry(entryType, entryName), - TarEntryFormat.Pax => new PaxTarEntry(entryType, entryName), - TarEntryFormat.Gnu => new GnuTarEntry(entryType, entryName), - _ => throw new InvalidDataException(SR.Format(SR.TarInvalidFormat, Format)), - }; + TarEntry entry = CreateEntryForFormat(entryType, entryName); entry._header._mTime = fileInfo.ftLastWriteTime.ToDateTimeUtc(); // We do not set atime and ctime by default because many external tools are unable to read GNU entries diff --git a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.cs b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.cs index 72b9f3b9d49d73..499e17f38ce4f9 100644 --- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.cs +++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarWriter.cs @@ -379,5 +379,16 @@ private static void ValidateEntryLinkName(TarEntryType entryType, string? linkNa } } } + + // Creates a new, empty entry of the type appropriate for the current archive Format. + private TarEntry CreateEntryForFormat(TarEntryType entryType, string entryName) => + Format switch + { + TarEntryFormat.V7 => new V7TarEntry(entryType, entryName), + TarEntryFormat.Ustar => new UstarTarEntry(entryType, entryName), + TarEntryFormat.Pax => new PaxTarEntry(entryType, entryName), + TarEntryFormat.Gnu => new GnuTarEntry(entryType, entryName), + _ => throw new InvalidDataException(SR.Format(SR.TarInvalidFormat, Format)), + }; } }