1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.commons.compress.archivers.tar;
21
22 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23 import static org.junit.jupiter.api.Assertions.assertEquals;
24 import static org.junit.jupiter.api.Assertions.assertFalse;
25 import static org.junit.jupiter.api.Assertions.assertNotNull;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27 import static org.junit.jupiter.api.Assertions.fail;
28 import static org.junit.jupiter.api.Assumptions.assumeFalse;
29
30 import java.io.BufferedInputStream;
31 import java.io.File;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.util.List;
37
38 import org.apache.commons.compress.AbstractTest;
39 import org.apache.commons.io.IOUtils;
40 import org.junit.jupiter.api.Test;
41 import org.junit.jupiter.api.condition.DisabledOnOs;
42 import org.junit.jupiter.api.condition.EnabledOnOs;
43 import org.junit.jupiter.api.condition.OS;
44
45 class SparseFilesTest extends AbstractTest {
46
47 private void assertPaxGNUEntry(final TarArchiveEntry entry, final String suffix) {
48 assertEquals("sparsefile-" + suffix, entry.getName());
49 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
50 assertTrue(entry.isGNUSparse());
51 assertTrue(entry.isPaxGNUSparse());
52 assertFalse(entry.isOldGNUSparse());
53
54 final List<TarArchiveStructSparse> sparseHeaders = entry.getSparseHeaders();
55 assertEquals(3, sparseHeaders.size());
56
57 assertEquals(0, sparseHeaders.get(0).getOffset());
58 assertEquals(2048, sparseHeaders.get(0).getNumbytes());
59
60 assertEquals(1050624L, sparseHeaders.get(1).getOffset());
61 assertEquals(2560, sparseHeaders.get(1).getNumbytes());
62
63 assertEquals(3101184L, sparseHeaders.get(2).getOffset());
64 assertEquals(0, sparseHeaders.get(2).getNumbytes());
65 }
66
67 private void assertPaxGNUEntry(final TarArchiveInputStream tin, final String suffix) throws Throwable {
68 final TarArchiveEntry ae = tin.getNextTarEntry();
69 assertEquals("sparsefile-" + suffix, ae.getName());
70 assertEquals(TarConstants.LF_NORMAL, ae.getLinkFlag());
71 assertTrue(ae.isGNUSparse());
72 assertTrue(ae.isPaxGNUSparse());
73 assertFalse(ae.isOldGNUSparse());
74 assertTrue(tin.canReadEntryData(ae));
75
76 final List<TarArchiveStructSparse> sparseHeaders = ae.getSparseHeaders();
77 assertEquals(3, sparseHeaders.size());
78
79 assertEquals(0, sparseHeaders.get(0).getOffset());
80 assertEquals(2048, sparseHeaders.get(0).getNumbytes());
81
82 assertEquals(1050624L, sparseHeaders.get(1).getOffset());
83 assertEquals(2560, sparseHeaders.get(1).getNumbytes());
84
85 assertEquals(3101184L, sparseHeaders.get(2).getOffset());
86 assertEquals(0, sparseHeaders.get(2).getNumbytes());
87 }
88
89 private InputStream extractTarAndGetInputStream(final File tarFile, final String sparseFileName) throws IOException, InterruptedException {
90 final ProcessBuilder pb = new ProcessBuilder("tar", "-xf", tarFile.getPath(), "-C", tempResultDir.getPath());
91 pb.redirectErrorStream(true);
92 final Process process = pb.start();
93
94 try (InputStream inputStream = process.getInputStream()) {
95 assertEquals(0, process.waitFor(), new String(IOUtils.toByteArray(inputStream)));
96 }
97 for (final File file : tempResultDir.listFiles()) {
98 if (file.getName().equals(sparseFileName)) {
99 return Files.newInputStream(file.toPath());
100 }
101 }
102 fail("didn't find " + sparseFileName + " after extracting " + tarFile);
103 return null;
104 }
105
106 private String getTarBinaryHelp() throws IOException {
107 final ProcessBuilder pb = new ProcessBuilder("tar", "--version");
108 pb.redirectErrorStream(true);
109 final Process process = pb.start();
110
111 try (InputStream inputStream = process.getInputStream()) {
112 return new String(IOUtils.toByteArray(inputStream));
113 }
114 }
115
116 @Test
117 void testCompareTarArchiveInputStreamWithTarFile() throws IOException {
118 final Path file = getPath("oldgnu_sparse.tar");
119 try (TarArchiveInputStream tarIn = new TarArchiveInputStream(new BufferedInputStream(Files.newInputStream(file)));
120 TarFile tarFile = new TarFile(file)) {
121 assertNotNull(tarIn.getNextTarEntry());
122 try (InputStream inputStream = tarFile.getInputStream(tarFile.getEntries().get(0))) {
123 assertArrayEquals(IOUtils.toByteArray(tarIn), IOUtils.toByteArray(inputStream));
124 }
125 }
126 }
127
128 @Test
129 @DisabledOnOs(OS.WINDOWS)
130 void testExtractExtendedOldGNU() throws IOException, InterruptedException {
131 final File file = getFile("oldgnu_extended_sparse.tar");
132 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparse6");
133 TarArchiveInputStream tin = new TarArchiveInputStream(Files.newInputStream(file.toPath()))) {
134 final TarArchiveEntry ae = tin.getNextTarEntry();
135 assertTrue(tin.canReadEntryData(ae));
136
137 assertArrayEquals(IOUtils.toByteArray(tin), IOUtils.toByteArray(sparseFileInputStream));
138
139 final List<TarArchiveStructSparse> sparseHeaders = ae.getOrderedSparseHeaders();
140 assertEquals(7, sparseHeaders.size());
141
142 assertEquals(0, sparseHeaders.get(0).getOffset());
143 assertEquals(1024, sparseHeaders.get(0).getNumbytes());
144
145 assertEquals(10240, sparseHeaders.get(1).getOffset());
146 assertEquals(1024, sparseHeaders.get(1).getNumbytes());
147
148 assertEquals(16384, sparseHeaders.get(2).getOffset());
149 assertEquals(1024, sparseHeaders.get(2).getNumbytes());
150
151 assertEquals(24576, sparseHeaders.get(3).getOffset());
152 assertEquals(1024, sparseHeaders.get(3).getNumbytes());
153
154 assertEquals(29696, sparseHeaders.get(4).getOffset());
155 assertEquals(1024, sparseHeaders.get(4).getNumbytes());
156
157 assertEquals(36864, sparseHeaders.get(5).getOffset());
158 assertEquals(1024, sparseHeaders.get(5).getNumbytes());
159
160 assertEquals(51200, sparseHeaders.get(6).getOffset());
161 assertEquals(0, sparseHeaders.get(6).getNumbytes());
162 }
163 }
164
165 @Test
166 @DisabledOnOs(OS.WINDOWS)
167 void testExtractOldGNU() throws IOException, InterruptedException {
168 try {
169 final File file = getFile("oldgnu_sparse.tar");
170 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile");
171 TarArchiveInputStream tin = new TarArchiveInputStream(Files.newInputStream(file.toPath()))) {
172 final TarArchiveEntry entry = tin.getNextTarEntry();
173 assertTrue(tin.canReadEntryData(entry));
174 assertArrayEquals(IOUtils.toByteArray(tin), IOUtils.toByteArray(sparseFileInputStream));
175 }
176 } catch (RuntimeException | IOException ex) {
177 ex.printStackTrace();
178 throw ex;
179 }
180 }
181
182 @Test
183 @DisabledOnOs(OS.WINDOWS)
184 void testExtractPaxGNU() throws IOException, InterruptedException {
185
186
187
188 assumeFalse(getTarBinaryHelp().startsWith("tar (GNU tar) 1.28"), "This test should be ignored if GNU tar is version 1.28");
189
190 final File file = getFile("pax_gnu_sparse.tar");
191 try (TarArchiveInputStream tin = new TarArchiveInputStream(Files.newInputStream(file.toPath()))) {
192
193 TarArchiveEntry paxGNUEntry = tin.getNextTarEntry();
194 assertTrue(tin.canReadEntryData(paxGNUEntry));
195 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-0.0")) {
196 assertArrayEquals(IOUtils.toByteArray(tin), IOUtils.toByteArray(sparseFileInputStream));
197 }
198
199 paxGNUEntry = tin.getNextTarEntry();
200 assertTrue(tin.canReadEntryData(paxGNUEntry));
201 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-0.1")) {
202 assertArrayEquals(IOUtils.toByteArray(tin), IOUtils.toByteArray(sparseFileInputStream));
203 }
204
205 paxGNUEntry = tin.getNextTarEntry();
206 assertTrue(tin.canReadEntryData(paxGNUEntry));
207 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-1.0")) {
208 assertArrayEquals(IOUtils.toByteArray(tin), IOUtils.toByteArray(sparseFileInputStream));
209 }
210 }
211 }
212
213 @Test
214 @EnabledOnOs(OS.WINDOWS)
215 void testExtractSparseTarsOnWindows() throws IOException {
216 final File oldGNUSparseTar = getFile("oldgnu_sparse.tar");
217 final File paxGNUSparseTar = getFile("pax_gnu_sparse.tar");
218 try (TarArchiveInputStream paxGNUSparseInputStream = new TarArchiveInputStream(Files.newInputStream(paxGNUSparseTar.toPath()))) {
219
220
221 TarArchiveEntry paxGNUEntry = paxGNUSparseInputStream.getNextTarEntry();
222 assertTrue(paxGNUSparseInputStream.canReadEntryData(paxGNUEntry));
223 try (TarArchiveInputStream oldGNUSparseInputStream = new TarArchiveInputStream(Files.newInputStream(oldGNUSparseTar.toPath()))) {
224 final TarArchiveEntry oldGNUEntry = oldGNUSparseInputStream.getNextTarEntry();
225 assertTrue(oldGNUSparseInputStream.canReadEntryData(oldGNUEntry));
226 assertArrayEquals(IOUtils.toByteArray(oldGNUSparseInputStream), IOUtils.toByteArray(paxGNUSparseInputStream));
227 }
228
229
230 paxGNUEntry = paxGNUSparseInputStream.getNextTarEntry();
231 assertTrue(paxGNUSparseInputStream.canReadEntryData(paxGNUEntry));
232 try (TarArchiveInputStream oldGNUSparseInputStream = new TarArchiveInputStream(Files.newInputStream(oldGNUSparseTar.toPath()))) {
233 final TarArchiveEntry oldGNUEntry = oldGNUSparseInputStream.getNextTarEntry();
234 assertTrue(oldGNUSparseInputStream.canReadEntryData(oldGNUEntry));
235 assertArrayEquals(IOUtils.toByteArray(oldGNUSparseInputStream), IOUtils.toByteArray(paxGNUSparseInputStream));
236 }
237
238
239 paxGNUEntry = paxGNUSparseInputStream.getNextTarEntry();
240 assertTrue(paxGNUSparseInputStream.canReadEntryData(paxGNUEntry));
241 try (TarArchiveInputStream oldGNUSparseInputStream = new TarArchiveInputStream(Files.newInputStream(oldGNUSparseTar.toPath()))) {
242 final TarArchiveEntry oldGNUEntry = oldGNUSparseInputStream.getNextTarEntry();
243 assertTrue(oldGNUSparseInputStream.canReadEntryData(oldGNUEntry));
244 assertArrayEquals(IOUtils.toByteArray(oldGNUSparseInputStream), IOUtils.toByteArray(paxGNUSparseInputStream));
245 }
246 }
247 }
248
249 @Test
250 void testOldGNU() throws Throwable {
251 final File file = getFile("oldgnu_sparse.tar");
252 try (TarArchiveInputStream tin = new TarArchiveInputStream(Files.newInputStream(file.toPath()))) {
253 final TarArchiveEntry ae = tin.getNextTarEntry();
254 assertEquals("sparsefile", ae.getName());
255 assertEquals(TarConstants.LF_GNUTYPE_SPARSE, ae.getLinkFlag());
256 assertTrue(ae.isOldGNUSparse());
257 assertTrue(ae.isGNUSparse());
258 assertFalse(ae.isPaxGNUSparse());
259 assertTrue(tin.canReadEntryData(ae));
260
261 final List<TarArchiveStructSparse> sparseHeaders = ae.getSparseHeaders();
262 assertEquals(4, sparseHeaders.size());
263
264 assertEquals(0, sparseHeaders.get(0).getOffset());
265 assertEquals(2048, sparseHeaders.get(0).getNumbytes());
266
267 assertEquals(1050624L, sparseHeaders.get(1).getOffset());
268 assertEquals(2560, sparseHeaders.get(1).getNumbytes());
269
270 assertEquals(3101184L, sparseHeaders.get(2).getOffset());
271 assertEquals(0, sparseHeaders.get(2).getNumbytes());
272
273 assertEquals(0, sparseHeaders.get(3).getOffset());
274 assertEquals(0, sparseHeaders.get(3).getNumbytes());
275
276 final List<TarArchiveStructSparse> sparseOrderedHeaders = ae.getOrderedSparseHeaders();
277 assertEquals(3, sparseOrderedHeaders.size());
278
279 assertEquals(0, sparseOrderedHeaders.get(0).getOffset());
280 assertEquals(2048, sparseOrderedHeaders.get(0).getNumbytes());
281
282 assertEquals(1050624L, sparseOrderedHeaders.get(1).getOffset());
283 assertEquals(2560, sparseOrderedHeaders.get(1).getNumbytes());
284
285 assertEquals(3101184L, sparseOrderedHeaders.get(2).getOffset());
286 assertEquals(0, sparseOrderedHeaders.get(2).getNumbytes());
287 }
288 }
289
290 @Test
291 void testPaxGNU() throws Throwable {
292 final File file = getFile("pax_gnu_sparse.tar");
293 try (TarArchiveInputStream tin = new TarArchiveInputStream(Files.newInputStream(file.toPath()))) {
294 assertPaxGNUEntry(tin, "0.0");
295 assertPaxGNUEntry(tin, "0.1");
296 assertPaxGNUEntry(tin, "1.0");
297 }
298 }
299
300 @Test
301 @DisabledOnOs(OS.WINDOWS)
302 void testTarFileExtractExtendedOldGNU() throws IOException, InterruptedException {
303 final File file = getFile("oldgnu_extended_sparse.tar");
304 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparse6");
305 TarFile tarFile = new TarFile(file)) {
306 final TarArchiveEntry ae = tarFile.getEntries().get(0);
307
308 try (InputStream tarInput = tarFile.getInputStream(ae)) {
309 assertArrayEquals(IOUtils.toByteArray(tarInput), IOUtils.toByteArray(sparseFileInputStream));
310 }
311
312 final List<TarArchiveStructSparse> sparseHeaders = ae.getOrderedSparseHeaders();
313 assertEquals(7, sparseHeaders.size());
314
315 assertEquals(0, sparseHeaders.get(0).getOffset());
316 assertEquals(1024, sparseHeaders.get(0).getNumbytes());
317
318 assertEquals(10240, sparseHeaders.get(1).getOffset());
319 assertEquals(1024, sparseHeaders.get(1).getNumbytes());
320
321 assertEquals(16384, sparseHeaders.get(2).getOffset());
322 assertEquals(1024, sparseHeaders.get(2).getNumbytes());
323
324 assertEquals(24576, sparseHeaders.get(3).getOffset());
325 assertEquals(1024, sparseHeaders.get(3).getNumbytes());
326
327 assertEquals(29696, sparseHeaders.get(4).getOffset());
328 assertEquals(1024, sparseHeaders.get(4).getNumbytes());
329
330 assertEquals(36864, sparseHeaders.get(5).getOffset());
331 assertEquals(1024, sparseHeaders.get(5).getNumbytes());
332
333 assertEquals(51200, sparseHeaders.get(6).getOffset());
334 assertEquals(0, sparseHeaders.get(6).getNumbytes());
335 }
336 }
337
338 @Test
339 @DisabledOnOs(OS.WINDOWS)
340 void testTarFileExtractOldGNU() throws IOException, InterruptedException {
341 final File file = getFile("oldgnu_sparse.tar");
342 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile");
343 TarFile tarFile = new TarFile(file)) {
344 final TarArchiveEntry entry = tarFile.getEntries().get(0);
345 try (InputStream tarInput = tarFile.getInputStream(entry)) {
346 assertArrayEquals(IOUtils.toByteArray(tarInput), IOUtils.toByteArray(sparseFileInputStream));
347 }
348 }
349 }
350
351 @Test
352 @DisabledOnOs(OS.WINDOWS)
353 void testTarFileExtractPaxGNU() throws IOException, InterruptedException {
354
355
356
357 assumeFalse(getTarBinaryHelp().startsWith("tar (GNU tar) 1.28"), "This test should be ignored if GNU tar is version 1.28");
358
359 final File file = getFile("pax_gnu_sparse.tar");
360 try (TarFile paxGnu = new TarFile(file)) {
361 final List<TarArchiveEntry> entries = paxGnu.getEntries();
362
363 TarArchiveEntry entry = entries.get(0);
364 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-0.0");
365 InputStream paxInput = paxGnu.getInputStream(entry)) {
366 assertArrayEquals(IOUtils.toByteArray(paxInput), IOUtils.toByteArray(sparseFileInputStream));
367 }
368
369 entry = entries.get(1);
370 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-0.1");
371 InputStream paxInput = paxGnu.getInputStream(entry)) {
372 assertArrayEquals(IOUtils.toByteArray(paxInput), IOUtils.toByteArray(sparseFileInputStream));
373 }
374
375 entry = entries.get(2);
376 try (InputStream sparseFileInputStream = extractTarAndGetInputStream(file, "sparsefile-1.0");
377 InputStream paxInput = paxGnu.getInputStream(entry)) {
378 assertArrayEquals(IOUtils.toByteArray(paxInput), IOUtils.toByteArray(sparseFileInputStream));
379 }
380 }
381 }
382
383 @Test
384 @EnabledOnOs(OS.WINDOWS)
385 void testTarFileExtractSparseTarsOnWindows() throws IOException {
386 final File oldGNUSparseTar = getFile("oldgnu_sparse.tar");
387 final File paxGNUSparseTar = getFile("pax_gnu_sparse.tar");
388 try (TarFile paxGnu = new TarFile(paxGNUSparseTar)) {
389 final List<TarArchiveEntry> entries = paxGnu.getEntries();
390
391
392 TarArchiveEntry paxGnuEntry = entries.get(0);
393 try (TarFile oldGnu = new TarFile(oldGNUSparseTar)) {
394 final TarArchiveEntry oldGnuEntry = oldGnu.getEntries().get(0);
395 try (InputStream old = oldGnu.getInputStream(oldGnuEntry);
396 InputStream pax = paxGnu.getInputStream(paxGnuEntry)) {
397 assertArrayEquals(IOUtils.toByteArray(old), IOUtils.toByteArray(pax));
398 }
399 }
400
401
402 paxGnuEntry = entries.get(1);
403 try (TarFile oldGnu = new TarFile(oldGNUSparseTar)) {
404 final TarArchiveEntry oldGnuEntry = oldGnu.getEntries().get(0);
405 try (InputStream old = oldGnu.getInputStream(oldGnuEntry);
406 InputStream pax = paxGnu.getInputStream(paxGnuEntry)) {
407 assertArrayEquals(IOUtils.toByteArray(old), IOUtils.toByteArray(pax));
408 }
409 }
410
411
412 paxGnuEntry = entries.get(2);
413 try (TarFile oldGnu = new TarFile(oldGNUSparseTar)) {
414 final TarArchiveEntry oldGnuEntry = oldGnu.getEntries().get(0);
415 try (InputStream old = oldGnu.getInputStream(oldGnuEntry);
416 InputStream pax = paxGnu.getInputStream(paxGnuEntry)) {
417 assertArrayEquals(IOUtils.toByteArray(old), IOUtils.toByteArray(pax));
418 }
419 }
420 }
421 }
422
423 @Test
424 void testTarFileOldGNU() throws Throwable {
425 final File file = getFile("oldgnu_sparse.tar");
426 try (TarFile tarFile = new TarFile(file)) {
427 final TarArchiveEntry ae = tarFile.getEntries().get(0);
428 assertEquals("sparsefile", ae.getName());
429 assertEquals(TarConstants.LF_GNUTYPE_SPARSE, ae.getLinkFlag());
430 assertTrue(ae.isOldGNUSparse());
431 assertTrue(ae.isGNUSparse());
432 assertFalse(ae.isPaxGNUSparse());
433
434 final List<TarArchiveStructSparse> sparseHeaders = ae.getSparseHeaders();
435 assertEquals(4, sparseHeaders.size());
436
437 assertEquals(0, sparseHeaders.get(0).getOffset());
438 assertEquals(2048, sparseHeaders.get(0).getNumbytes());
439
440 assertEquals(1050624L, sparseHeaders.get(1).getOffset());
441 assertEquals(2560, sparseHeaders.get(1).getNumbytes());
442
443 assertEquals(3101184L, sparseHeaders.get(2).getOffset());
444 assertEquals(0, sparseHeaders.get(2).getNumbytes());
445
446 assertEquals(0, sparseHeaders.get(3).getOffset());
447 assertEquals(0, sparseHeaders.get(3).getNumbytes());
448
449 final List<TarArchiveStructSparse> sparseOrderedHeaders = ae.getOrderedSparseHeaders();
450 assertEquals(3, sparseOrderedHeaders.size());
451
452 assertEquals(0, sparseOrderedHeaders.get(0).getOffset());
453 assertEquals(2048, sparseOrderedHeaders.get(0).getNumbytes());
454
455 assertEquals(1050624L, sparseOrderedHeaders.get(1).getOffset());
456 assertEquals(2560, sparseOrderedHeaders.get(1).getNumbytes());
457
458 assertEquals(3101184L, sparseOrderedHeaders.get(2).getOffset());
459 assertEquals(0, sparseOrderedHeaders.get(2).getNumbytes());
460 }
461 }
462
463 @Test
464 void testTarFilePaxGNU() throws IOException {
465 final File file = getFile("pax_gnu_sparse.tar");
466 try (TarFile tarFile = new TarFile(file)) {
467 final List<TarArchiveEntry> entries = tarFile.getEntries();
468 assertPaxGNUEntry(entries.get(0), "0.0");
469 assertPaxGNUEntry(entries.get(1), "0.1");
470 assertPaxGNUEntry(entries.get(2), "1.0");
471 }
472 }
473 }