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