Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool o

string destinationDirectoryFullPath = destinationDirectoryPath.EndsWith(Path.DirectorySeparatorChar) ? destinationDirectoryPath : destinationDirectoryPath + Path.DirectorySeparatorChar;

string fileDestinationPath = GetSanitizedFullPath(destinationDirectoryFullPath, Name, SR.TarExtractingResultsFileOutside);
string? fileDestinationPath = GetSanitizedFullPath(destinationDirectoryFullPath, Name);
if (fileDestinationPath == null)
{
throw new IOException(string.Format(SR.TarExtractingResultsFileOutside, Name, destinationDirectoryFullPath));
}

string? linkTargetPath = null;
if (EntryType is TarEntryType.SymbolicLink or TarEntryType.HardLink)
Expand All @@ -273,7 +277,11 @@ internal void ExtractRelativeToDirectory(string destinationDirectoryPath, bool o
throw new FormatException(SR.TarEntryHardLinkOrSymlinkLinkNameEmpty);
}

linkTargetPath = GetSanitizedFullPath(destinationDirectoryFullPath, LinkName, SR.TarExtractingResultsLinkOutside);
linkTargetPath = GetSanitizedFullPath(destinationDirectoryFullPath, LinkName);
if (linkTargetPath == null)
{
throw new IOException(string.Format(SR.TarExtractingResultsLinkOutside, LinkName, destinationDirectoryFullPath));
}
}

if (EntryType == TarEntryType.Directory)
Expand All @@ -286,26 +294,16 @@ 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));
}
// 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 actualPath = Path.IsPathFullyQualified(path) ? path : Path.Combine(destinationDirectoryFullPath, path);
actualPath = Path.Join(Path.GetDirectoryName(actualPath), ArchivingUtils.SanitizeEntryFilePath(Path.GetFileName(actualPath)));
actualPath = Path.GetFullPath(actualPath); // Normalizes relative segments

return actualPath;
}
return actualPath.StartsWith(destinationDirectoryFullPath, PathInternal.StringComparison) ? actualPath : null;
}

// Extracts the current entry into the filesystem, regardless of the entry type.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 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 Microsoft.DotNet.RemoteExecutor;
using System.IO;
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -52,7 +52,7 @@ public void ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries()
string fileWithTwoSegments = Path.Join(secondSegment, "c.txt");

using MemoryStream archive = new MemoryStream();
using (TarWriter writer = new TarWriter(archive, TarFormat.Ustar, leaveOpen: true))
using (TarWriter writer = new TarWriter(archive, TarFormat.Pax, leaveOpen: true))
{
// No preceding directory entries for the segments
UstarTarEntry entry = new UstarTarEntry(TarEntryType.RegularFile, fileWithTwoSegments);
Expand All @@ -78,7 +78,7 @@ public void ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries()
public void Extract_LinkEntry_TargetOutsideDirectory(TarEntryType entryType)
{
using MemoryStream archive = new MemoryStream();
using (TarWriter writer = new TarWriter(archive, TarFormat.Ustar, leaveOpen: true))
using (TarWriter writer = new TarWriter(archive, TarFormat.Pax, leaveOpen: true))
{
UstarTarEntry entry = new UstarTarEntry(entryType, "link");
entry.LinkName = PlatformDetection.IsWindows ? @"C:\Windows\System32\notepad.exe" : "/usr/bin/nano";
Expand All @@ -97,8 +97,9 @@ 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)]
private static bool SupportsHardLinkCreation => !PlatformDetection.IsAndroid;

[ConditionalFact(nameof(SupportsHardLinkCreation))]
public void Extract_HardLinkEntry_TargetInsideDirectory() => Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType.HardLink);

private void Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType entryType)
Expand All @@ -112,7 +113,7 @@ private void Extract_LinkEntry_TargetInsideDirectory_Internal(TarEntryType entry
File.Create(targetPath).Dispose();

using MemoryStream archive = new MemoryStream();
using (TarWriter writer = new TarWriter(archive, TarFormat.Ustar, leaveOpen: true))
using (TarWriter writer = new TarWriter(archive, TarFormat.Pax, leaveOpen: true))
{
UstarTarEntry entry = new UstarTarEntry(entryType, linkName);
entry.LinkName = targetPath;
Expand Down