Skip to content

Commit cddf8e6

Browse files
Enable filtering of tarball extraction (#15717)
2 parents f6ba6dd + 3b2ceb2 commit cddf8e6

File tree

1 file changed

+72
-12
lines changed

1 file changed

+72
-12
lines changed

src/core-sdk-tasks/ExtractArchiveToDirectory.cs

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
using Microsoft.Build.Framework;
66
using Microsoft.Build.Utilities;
77
using System;
8+
#if !NETFRAMEWORK
9+
using System.Formats.Tar;
10+
#endif
811
using System.IO;
912
using System.IO.Compression;
13+
using System.Linq;
1014

1115
namespace Microsoft.DotNet.Build.Tasks
1216
{
@@ -33,7 +37,7 @@ public sealed class ExtractArchiveToDirectory : ToolTask
3337
public bool CleanDestination { get; set; }
3438

3539
/// <summary>
36-
/// A list of directories, semicolon deliminated, relative to the root of the archive to include. If empty all directories will be copied.
40+
/// A list of directories, relative to the root of the archive to include. If empty all directories will be copied.
3741
/// </summary>
3842
public ITaskItem[] DirectoriesToCopy { get; set; }
3943

@@ -71,22 +75,26 @@ protected override bool ValidateParameters()
7175
public override bool Execute()
7276
{
7377
bool retVal = true;
78+
bool isZipArchive = Path.GetExtension(SourceArchive).Equals(".zip", StringComparison.OrdinalIgnoreCase);
79+
bool isTarballArchive = SourceArchive.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase);
7480

75-
// Inherits from ToolTask in order to shell out to tar.
81+
// Inherits from ToolTask in order to shell out to tar for complete extraction
7682
// If the file is a .zip, then don't call the base Execute method, just run as a normal task
77-
if (Path.GetExtension(SourceArchive).Equals(".zip", StringComparison.OrdinalIgnoreCase))
83+
// If the file is a .tar.gz, and DirectoriesToCopy isn't empty, also run a normal task.
84+
if (isZipArchive || isTarballArchive)
7885
{
7986
if (ValidateParameters())
8087
{
8188
if (DirectoriesToCopy != null && DirectoriesToCopy.Length != 0)
8289
{
83-
var zip = new ZipArchive(File.OpenRead(SourceArchive));
84-
string loc = DestinationDirectory;
85-
foreach (var entry in zip.Entries)
90+
// Partial archive extraction
91+
if (isZipArchive)
8692
{
87-
foreach (var directory in DirectoriesToCopy)
93+
var zip = new ZipArchive(File.OpenRead(SourceArchive));
94+
string loc = DestinationDirectory;
95+
foreach (var entry in zip.Entries)
8896
{
89-
if (entry.FullName.StartsWith(directory.ItemSpec))
97+
if (ShouldExtractItem(entry.FullName))
9098
{
9199
if (!Directory.Exists(Path.Combine(DestinationDirectory, Path.GetDirectoryName(entry.FullName))))
92100
{
@@ -98,16 +106,57 @@ public override bool Execute()
98106
}
99107
}
100108
}
109+
else
110+
{
111+
#if NETFRAMEWORK
112+
// Run the base tool, which uses external 'tar' command
113+
retVal = base.Execute();
114+
#else
115+
// Decompress GZip content
116+
using FileStream compressedFileStream = File.Open(SourceArchive, FileMode.Open);
117+
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
118+
using var decompressedStream = new MemoryStream();
119+
decompressor.CopyTo(decompressedStream);
120+
decompressedStream.Seek(0, SeekOrigin.Begin);
121+
122+
// Extract Tar content
123+
using TarReader tr = new TarReader(decompressedStream);
124+
while (tr.GetNextEntry() is TarEntry tarEntry)
125+
{
126+
if (tarEntry.EntryType != TarEntryType.Directory)
127+
{
128+
string entryName = tarEntry.Name;
129+
entryName = entryName.StartsWith("./") ? entryName[2..] : entryName;
130+
if (ShouldExtractItem(entryName))
131+
{
132+
Log.LogMessage(entryName);
133+
string destinationPath = Path.Combine(DestinationDirectory, entryName);
134+
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
135+
tarEntry.ExtractToFile(destinationPath, overwrite: true);
136+
}
137+
}
138+
}
139+
#endif
140+
}
101141
}
102142
else
103-
{
143+
{
144+
// Complete archive extraction
145+
if (isZipArchive)
146+
{
104147
#if NETFRAMEWORK
105-
// .NET Framework doesn't have overload to overwrite files
106-
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory);
148+
// .NET Framework doesn't have overload to overwrite files
149+
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory);
107150
#else
108151

109-
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory, overwriteFiles: true);
152+
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory, overwriteFiles: true);
110153
#endif
154+
}
155+
else
156+
{
157+
// Run the base tool, which uses external 'tar' command
158+
retVal = base.Execute();
159+
}
111160
}
112161
}
113162
else
@@ -129,6 +178,17 @@ public override bool Execute()
129178
return retVal;
130179
}
131180

181+
private bool ShouldExtractItem(string path)
182+
{
183+
if (DirectoriesToCopy != null)
184+
{
185+
return DirectoriesToCopy.Any(p => path.StartsWith(p.ItemSpec));
186+
187+
}
188+
189+
return false;
190+
}
191+
132192
protected override string ToolName
133193
{
134194
get { return "tar"; }

0 commit comments

Comments
 (0)