-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
Copy pathTarFile.ExtractToDirectoryAsync.Stream.Tests.cs
223 lines (188 loc) · 10.6 KB
/
TarFile.ExtractToDirectoryAsync.Stream.Tests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// 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.IO.Compression;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace System.Formats.Tar.Tests
{
public class TarFile_ExtractToDirectoryAsync_Stream_Tests : TarTestsBase
{
[Fact]
public async Task ExtractToDirectoryAsync_Cancel()
{
CancellationTokenSource cs = new CancellationTokenSource();
cs.Cancel();
using (MemoryStream archiveStream = new MemoryStream())
{
await Assert.ThrowsAsync<TaskCanceledException>(() => TarFile.ExtractToDirectoryAsync(archiveStream, "directory", overwriteFiles: true, cs.Token));
}
}
[Fact]
public Task NullStream_Throws_Async() =>
Assert.ThrowsAsync<ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(source: null, destinationDirectoryName: "path", overwriteFiles: false));
[Fact]
public async Task InvalidPath_Throws_Async()
{
using (MemoryStream archive = new MemoryStream())
{
await Assert.ThrowsAsync<ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: null, overwriteFiles: false));
await Assert.ThrowsAsync<ArgumentException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: string.Empty, overwriteFiles: false));
}
}
[Fact]
public async Task UnreadableStream_Throws_Async()
{
using (MemoryStream archive = new MemoryStream())
{
using (WrappedStream unreadable = new WrappedStream(archive, canRead: false, canWrite: true, canSeek: true))
{
await Assert.ThrowsAsync<ArgumentException>(() => TarFile.ExtractToDirectoryAsync(unreadable, destinationDirectoryName: "path", overwriteFiles: false));
}
}
}
[Fact]
public async Task NonExistentDirectory_Throws_Async()
{
using (TempDirectory root = new TempDirectory())
{
string dirPath = Path.Join(root.Path, "dir");
using (MemoryStream archive = new MemoryStream())
{
await Assert.ThrowsAsync<DirectoryNotFoundException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: dirPath, overwriteFiles: false));
}
}
}
[Fact]
public async Task ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries_Async()
{
using (TempDirectory root = new TempDirectory())
{
string firstSegment = "a";
string secondSegment = Path.Join(firstSegment, "b");
string fileWithTwoSegments = Path.Join(secondSegment, "c.txt");
await using (MemoryStream archive = new MemoryStream())
{
await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true))
{
// No preceding directory entries for the segments
UstarTarEntry entry = new UstarTarEntry(TarEntryType.RegularFile, fileWithTwoSegments);
entry.DataStream = new MemoryStream();
entry.DataStream.Write(new byte[] { 0x1 });
entry.DataStream.Seek(0, SeekOrigin.Begin);
await writer.WriteEntryAsync(entry);
}
archive.Seek(0, SeekOrigin.Begin);
await TarFile.ExtractToDirectoryAsync(archive, root.Path, overwriteFiles: false);
Assert.True(Directory.Exists(Path.Join(root.Path, firstSegment)));
Assert.True(Directory.Exists(Path.Join(root.Path, secondSegment)));
Assert.True(File.Exists(Path.Join(root.Path, fileWithTwoSegments)));
}
}
}
[Fact]
public async Task ExtractEntry_DockerImageTarWithFileTypeInDirectoriesInMode_SuccessfullyExtracts_Async()
{
using (TempDirectory root = new TempDirectory())
{
await using MemoryStream archiveStream = GetTarMemoryStream(CompressionMethod.Uncompressed, "golang_tar", "docker-hello-world");
await TarFile.ExtractToDirectoryAsync(archiveStream, root.Path, overwriteFiles: true);
Assert.True(File.Exists(Path.Join(root.Path, "manifest.json")));
Assert.True(File.Exists(Path.Join(root.Path, "repositories")));
}
}
[Theory]
[InlineData(TarEntryType.SymbolicLink)]
[InlineData(TarEntryType.HardLink)]
public async Task Extract_LinkEntry_TargetOutsideDirectory_Async(TarEntryType entryType)
{
await using (MemoryStream archive = new MemoryStream())
{
await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true))
{
UstarTarEntry entry = new UstarTarEntry(entryType, "link");
entry.LinkName = PlatformDetection.IsWindows ? @"C:\Windows\System32\notepad.exe" : "/usr/bin/nano";
await writer.WriteEntryAsync(entry);
}
archive.Seek(0, SeekOrigin.Begin);
using (TempDirectory root = new TempDirectory())
{
await Assert.ThrowsAsync<IOException>(() => TarFile.ExtractToDirectoryAsync(archive, root.Path, overwriteFiles: false));
Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count());
}
}
}
[ConditionalTheory(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
[InlineData(TarEntryFormat.Pax)]
[InlineData(TarEntryFormat.Gnu)]
public Task Extract_SymbolicLinkEntry_TargetInsideDirectory_Async(TarEntryFormat format) => Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType.SymbolicLink, format, null);
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.SupportsHardLinkCreation))]
[InlineData(TarEntryFormat.Pax)]
[InlineData(TarEntryFormat.Gnu)]
public Task Extract_HardLinkEntry_TargetInsideDirectory_Async(TarEntryFormat format) => Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType.HardLink, format, null);
[ConditionalTheory(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
[InlineData(TarEntryFormat.Pax)]
[InlineData(TarEntryFormat.Gnu)]
public Task Extract_SymbolicLinkEntry_TargetInsideDirectory_LongBaseDir_Async(TarEntryFormat format) => Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType.SymbolicLink, format, new string('a', 99));
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.SupportsHardLinkCreation))]
[InlineData(TarEntryFormat.Pax)]
[InlineData(TarEntryFormat.Gnu)]
public Task Extract_HardLinkEntry_TargetInsideDirectory_LongBaseDir_Async(TarEntryFormat format) => Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType.HardLink, format, new string('a', 99));
// This test would not pass for the V7 and Ustar formats in some OSs like MacCatalyst, tvOSSimulator and OSX, because the TempDirectory gets created in
// a folder with a path longer than 100 bytes, and those tar formats have no way of handling pathnames and linknames longer than that length.
// The rest of the OSs create the TempDirectory in a path that does not surpass the 100 bytes, so the 'subfolder' parameter gives a chance to extend
// the base directory past that length, to ensure this scenario is tested everywhere.
private async Task Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType entryType, TarEntryFormat format, string subfolder)
{
using (TempDirectory root = new TempDirectory())
{
string baseDir = string.IsNullOrEmpty(subfolder) ? root.Path : Path.Join(root.Path, subfolder);
Directory.CreateDirectory(baseDir);
string linkName = "link";
string targetName = "target";
string targetPath = Path.Join(baseDir, targetName);
File.Create(targetPath).Dispose();
await using (MemoryStream archive = new MemoryStream())
{
await using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true))
{
TarEntry entry = InvokeTarEntryCreationConstructor(format, entryType, linkName);
entry.LinkName = targetPath;
await writer.WriteEntryAsync(entry);
}
archive.Seek(0, SeekOrigin.Begin);
await TarFile.ExtractToDirectoryAsync(archive, baseDir, overwriteFiles: false);
Assert.Equal(2, Directory.GetFileSystemEntries(baseDir).Count());
}
}
}
[Theory]
[InlineData(512)]
[InlineData(512 + 1)]
[InlineData(512 + 512 - 1)]
public async Task Extract_UnseekableStream_BlockAlignmentPadding_DoesNotAffectNextEntries_Async(int contentSize)
{
byte[] fileContents = new byte[contentSize];
Array.Fill<byte>(fileContents, 0x1);
using var archive = new MemoryStream();
using (var compressor = new GZipStream(archive, CompressionMode.Compress, leaveOpen: true))
{
using var writer = new TarWriter(compressor);
var entry1 = new PaxTarEntry(TarEntryType.RegularFile, "file");
entry1.DataStream = new MemoryStream(fileContents);
await writer.WriteEntryAsync(entry1);
var entry2 = new PaxTarEntry(TarEntryType.RegularFile, "next-file");
await writer.WriteEntryAsync(entry2);
}
archive.Position = 0;
using var decompressor = new GZipStream(archive, CompressionMode.Decompress);
using var reader = new TarReader(decompressor);
using TempDirectory destination = new TempDirectory();
await TarFile.ExtractToDirectoryAsync(decompressor, destination.Path, overwriteFiles: true);
Assert.Equal(2, Directory.GetFileSystemEntries(destination.Path, "*", SearchOption.AllDirectories).Count());
}
}
}