diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index d629df7063f7b4152bf091809d1bf4b08e08fc15..92accd2d9647bd5eef6f1305f7c095e7ac4b01a4 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -138,9 +138,9 @@
https://github.com/dotnet/runtime-assets
0920468fa7db4ee8ea8bbcba186421cb92713adf
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
https://github.com/dotnet/runtime-assets
diff --git a/eng/Versions.props b/eng/Versions.props
index c9cd5414c496b9eabf0ff8b8fb4b24f0859df436..479b25b159147b6c2bb80824cd87344479c5f879 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -120,7 +120,7 @@
7.0.0-beta.22281.1
7.0.0-beta.22281.1
7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
+ 7.0.0-beta.22313.1
7.0.0-beta.22281.1
7.0.0-beta.22281.1
7.0.0-beta.22281.1
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
index f2c5481509817b920939296f2973ecbac029f645..470a097e526f805e1e41fce9a885eed3334b2c5f 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
@@ -238,6 +238,7 @@ private static bool GetAlpnSupport()
public static bool SupportsAlpn => s_supportsAlpn.Value;
public static bool SupportsClientAlpn => SupportsAlpn || IsOSX || IsMacCatalyst || IsiOS || IstvOS;
+ public static bool SupportsHardLinkCreation => !IsAndroid;
private static readonly Lazy s_supportsTls10 = new Lazy(GetTls10Support);
private static readonly Lazy s_supportsTls11 = new Lazy(GetTls11Support);
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 a2328ea9691a3ca3b6946d56d984f889441e30c3..ca1e482cb826397b829b3fbd8532345e95a12e49 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
@@ -261,9 +261,13 @@ internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool o
Debug.Assert(!string.IsNullOrEmpty(destinationDirectoryPath));
Debug.Assert(Path.IsPathFullyQualified(destinationDirectoryPath));
- string destinationDirectoryFullPath = destinationDirectoryPath.EndsWith(Path.DirectorySeparatorChar) ? destinationDirectoryPath : destinationDirectoryPath + Path.DirectorySeparatorChar;
+ destinationDirectoryPath = Path.TrimEndingDirectorySeparator(destinationDirectoryPath);
- string fileDestinationPath = GetSanitizedFullPath(destinationDirectoryFullPath, Name, SR.TarExtractingResultsFileOutside);
+ string? fileDestinationPath = GetSanitizedFullPath(destinationDirectoryPath, Name);
+ if (fileDestinationPath == null)
+ {
+ throw new IOException(string.Format(SR.TarExtractingResultsFileOutside, Name, destinationDirectoryPath));
+ }
string? linkTargetPath = null;
if (EntryType is TarEntryType.SymbolicLink or TarEntryType.HardLink)
@@ -273,7 +277,11 @@ internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool o
throw new FormatException(SR.TarEntryHardLinkOrSymlinkLinkNameEmpty);
}
- linkTargetPath = GetSanitizedFullPath(destinationDirectoryFullPath, LinkName, SR.TarExtractingResultsLinkOutside);
+ linkTargetPath = GetSanitizedFullPath(destinationDirectoryPath, LinkName);
+ if (linkTargetPath == null)
+ {
+ throw new IOException(string.Format(SR.TarExtractingResultsLinkOutside, LinkName, destinationDirectoryPath));
+ }
}
if (EntryType == TarEntryType.Directory)
@@ -286,26 +294,15 @@ internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool o
Directory.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!);
ExtractToFileInternal(fileDestinationPath, linkTargetPath, overwrite);
}
+ }
- // If the path can be extracted in the specified destination directory, returns the full path with sanitized file name. Otherwise, throws.
- static string GetSanitizedFullPath(string destinationDirectoryFullPath, string path, string exceptionMessage)
- {
- string actualPath = Path.Join(Path.GetDirectoryName(path), ArchivingUtils.SanitizeEntryFilePath(Path.GetFileName(path)));
-
- if (!Path.IsPathFullyQualified(actualPath))
- {
- actualPath = Path.Combine(destinationDirectoryFullPath, actualPath);
- }
-
- actualPath = Path.GetFullPath(actualPath);
-
- if (!actualPath.StartsWith(destinationDirectoryFullPath, PathInternal.StringComparison))
- {
- throw new IOException(string.Format(exceptionMessage, path, destinationDirectoryFullPath));
- }
-
- return actualPath;
- }
+ // If the path can be extracted in the specified destination directory, returns the full path with sanitized file name. Otherwise, returns null.
+ private static string? GetSanitizedFullPath(string destinationDirectoryFullPath, string path)
+ {
+ string fullyQualifiedPath = Path.IsPathFullyQualified(path) ? path : Path.Combine(destinationDirectoryFullPath, path);
+ string normalizedPath = Path.GetFullPath(fullyQualifiedPath); // Removes relative segments
+ string sanitizedPath = Path.Join(Path.GetDirectoryName(normalizedPath), ArchivingUtils.SanitizeEntryFilePath(Path.GetFileName(normalizedPath)));
+ return sanitizedPath.StartsWith(destinationDirectoryFullPath, PathInternal.StringComparison) ? sanitizedPath : null;
}
// Extracts the current entry into the filesystem, regardless of the entry type.
diff --git a/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj b/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
index 3b53355dd6b46b1a688c237d3d87737bfc842a87..95cbdd4e41e811063dfd949cf5de587f16bae322 100644
--- a/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
+++ b/src/libraries/System.Formats.Tar/tests/System.Formats.Tar.Tests.csproj
@@ -17,6 +17,7 @@
+
@@ -52,6 +53,7 @@
+
diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
index 70453f4d49931ee8fa66bbc2becd2eeef57248d7..a753358cb8a3940db124a9b5fd8219366725b458 100644
--- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.File.Tests.cs
@@ -136,5 +136,28 @@ public void Extract_AllSegmentsOfPath()
string filePath = Path.Join(segment2Path, "file.txt");
Assert.True(File.Exists(filePath), $"{filePath}' does not exist.");
}
+
+ [Fact]
+ public void ExtractArchiveWithEntriesThatStartWithSlashDotPrefix()
+ {
+ using TempDirectory root = new TempDirectory();
+
+ using MemoryStream archiveStream = GetStrangeTarMemoryStream("prefixDotSlashAndCurrentFolderEntry");
+
+ TarFile.ExtractToDirectory(archiveStream, root.Path, overwriteFiles: true);
+
+ archiveStream.Position = 0;
+
+ using TarReader reader = new TarReader(archiveStream, leaveOpen: false);
+
+ TarEntry entry;
+ while ((entry = reader.GetNextEntry()) != null)
+ {
+ // Normalize the path (remove redundant segments), remove trailing separators
+ // this is so the first entry can be skipped if it's the same as the root directory
+ string entryPath = Path.TrimEndingDirectorySeparator(Path.GetFullPath(Path.Join(root.Path, entry.Name)));
+ Assert.True(Path.Exists(entryPath), $"Entry was not extracted: {entryPath}");
+ }
+ }
}
}
diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs
index 8b9af8b3bc3e26477e8b2877e9ab2f9e848a312e..9464e92488795e0891eb1d10bfb834b7ecd31ba3 100644
--- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs
@@ -97,8 +97,7 @@ public void Extract_LinkEntry_TargetOutsideDirectory(TarEntryType entryType)
[ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
public void Extract_SymbolicLinkEntry_TargetInsideDirectory() => Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType.SymbolicLink);
- [Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/68360", TestPlatforms.Android | TestPlatforms.LinuxBionic | TestPlatforms.iOS | TestPlatforms.tvOS)]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsHardLinkCreation))]
public void Extract_HardLinkEntry_TargetInsideDirectory() => Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType.HardLink);
private void Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType entryType)
diff --git a/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.Unix.cs b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.Unix.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a4333bb21792854842a8bd93add4feaa71627395
--- /dev/null
+++ b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.Unix.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Xunit;
+
+namespace System.Formats.Tar.Tests
+{
+ public partial class TarReader_ExtractToFile_Tests : TarTestsBase
+ {
+ [Fact]
+ public void ExtractToFile_SpecialFile_Unelevated_Throws()
+ {
+ using TempDirectory root = new TempDirectory();
+ using MemoryStream ms = GetTarMemoryStream(CompressionMethod.Uncompressed, TestTarFormat.ustar, "specialfiles");
+
+ using TarReader reader = new TarReader(ms);
+
+ string path = Path.Join(root.Path, "output");
+
+ // Block device requires elevation for writing
+ PosixTarEntry blockDevice = reader.GetNextEntry() as PosixTarEntry;
+ Assert.NotNull(blockDevice);
+ Assert.Throws(() => blockDevice.ExtractToFile(path, overwrite: false));
+ Assert.False(File.Exists(path));
+
+ // Character device requires elevation for writing
+ PosixTarEntry characterDevice = reader.GetNextEntry() as PosixTarEntry;
+ Assert.NotNull(characterDevice);
+ Assert.Throws(() => characterDevice.ExtractToFile(path, overwrite: false));
+ Assert.False(File.Exists(path));
+
+ // Fifo does not require elevation, should succeed
+ PosixTarEntry fifo = reader.GetNextEntry() as PosixTarEntry;
+ Assert.NotNull(fifo);
+ fifo.ExtractToFile(path, overwrite: false);
+ Assert.True(File.Exists(path));
+
+ Assert.Null(reader.GetNextEntry());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.cs
index bf5b6a110b570cfe356570b91f2203766f915f49..c347c5e4fdc3d001a29b4457b358c3a6da2cc171 100644
--- a/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarReader/TarReader.ExtractToFile.Tests.cs
@@ -1,44 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
using Xunit;
namespace System.Formats.Tar.Tests
{
- public class TarReader_ExtractToFile_Tests : TarTestsBase
+ public partial class TarReader_ExtractToFile_Tests : TarTestsBase
{
[Fact]
- public void ExtractToFile_SpecialFile_Unelevated_Throws()
+ public void ExtractEntriesWithSlashDotPrefix()
{
using TempDirectory root = new TempDirectory();
- using MemoryStream ms = GetTarMemoryStream(CompressionMethod.Uncompressed, TestTarFormat.ustar, "specialfiles");
- using TarReader reader = new TarReader(ms);
-
- string path = Path.Join(root.Path, "output");
-
- // Block device requires elevation for writing
- PosixTarEntry blockDevice = reader.GetNextEntry() as PosixTarEntry;
- Assert.NotNull(blockDevice);
- Assert.Throws(() => blockDevice.ExtractToFile(path, overwrite: false));
- Assert.False(File.Exists(path));
-
- // Character device requires elevation for writing
- PosixTarEntry characterDevice = reader.GetNextEntry() as PosixTarEntry;
- Assert.NotNull(characterDevice);
- Assert.Throws(() => characterDevice.ExtractToFile(path, overwrite: false));
- Assert.False(File.Exists(path));
-
- // Fifo does not require elevation, should succeed
- PosixTarEntry fifo = reader.GetNextEntry() as PosixTarEntry;
- Assert.NotNull(fifo);
- fifo.ExtractToFile(path, overwrite: false);
- Assert.True(File.Exists(path));
-
- Assert.Null(reader.GetNextEntry());
+ using MemoryStream archiveStream = GetStrangeTarMemoryStream("prefixDotSlashAndCurrentFolderEntry");
+ using (TarReader reader = new TarReader(archiveStream, leaveOpen: false))
+ {
+ string rootPath = Path.TrimEndingDirectorySeparator(root.Path);
+ TarEntry entry;
+ while ((entry = reader.GetNextEntry()) != null)
+ {
+ Assert.NotNull(entry);
+ Assert.StartsWith("./", entry.Name);
+ // Normalize the path (remove redundant segments), remove trailing separators
+ // this is so the first entry can be skipped if it's the same as the root directory
+ string entryPath = Path.TrimEndingDirectorySeparator(Path.GetFullPath(Path.Join(rootPath, entry.Name)));
+ if (entryPath != rootPath)
+ {
+ entry.ExtractToFile(entryPath, overwrite: true);
+ Assert.True(Path.Exists(entryPath), $"Entry was not extracted: {entryPath}");
+ }
+ }
+ }
}
+
}
}
\ No newline at end of file
diff --git a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
index fb0467593d18e18121a0c635825f27f921b9af3f..a4c19ddb78402eb1a0c57e2159eb4de1811030a0 100644
--- a/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
+++ b/src/libraries/System.Formats.Tar/tests/TarTestsBase.cs
@@ -96,9 +96,17 @@ protected static string GetTarFilePath(CompressionMethod compressionMethod, Test
}
// MemoryStream containing the copied contents of the specified file. Meant for reading and writing.
- protected static MemoryStream GetTarMemoryStream(CompressionMethod compressionMethod, TestTarFormat format, string testCaseName)
+ protected static MemoryStream GetTarMemoryStream(CompressionMethod compressionMethod, TestTarFormat format, string testCaseName) =>
+ GetMemoryStream(GetTarFilePath(compressionMethod, format, testCaseName));
+
+ protected static string GetStrangeTarFilePath(string testCaseName) =>
+ Path.Join(Directory.GetCurrentDirectory(), "strange", testCaseName + ".tar");
+
+ protected static MemoryStream GetStrangeTarMemoryStream(string testCaseName) =>
+ GetMemoryStream(GetStrangeTarFilePath(testCaseName));
+
+ private static MemoryStream GetMemoryStream(string path)
{
- string path = GetTarFilePath(compressionMethod, format, testCaseName);
MemoryStream ms = new();
using (FileStream fs = File.OpenRead(path))
{