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 java.nio.charset.StandardCharsets.UTF_8;
23 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertFalse;
26 import static org.junit.jupiter.api.Assertions.assertNotNull;
27 import static org.junit.jupiter.api.Assertions.assertNull;
28 import static org.junit.jupiter.api.Assertions.assertThrows;
29 import static org.junit.jupiter.api.Assertions.assertTrue;
30
31 import java.io.ByteArrayInputStream;
32 import java.io.ByteArrayOutputStream;
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.io.OutputStream;
38 import java.io.Reader;
39 import java.nio.file.Files;
40 import java.security.MessageDigest;
41 import java.util.Calendar;
42 import java.util.Date;
43 import java.util.HashMap;
44 import java.util.Map;
45 import java.util.TimeZone;
46
47 import org.apache.commons.compress.AbstractTest;
48 import org.apache.commons.compress.archivers.ArchiveEntry;
49 import org.apache.commons.compress.archivers.ArchiveOutputStream;
50 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.io.output.NullOutputStream;
53 import org.apache.commons.lang3.ArrayFill;
54 import org.junit.jupiter.api.Disabled;
55 import org.junit.jupiter.api.Test;
56
57 class TarArchiveOutputStreamTest extends AbstractTest {
58
59 private static byte[] createTarArchiveContainingOneDirectory(final String fileName, final Date modificationDate) throws IOException {
60 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
61 final TarArchiveOutputStream ref;
62 try (TarArchiveOutputStream outputStream = new TarArchiveOutputStream(baos, 1024)) {
63 ref = outputStream;
64 outputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
65 final TarArchiveEntry tarEntry = new TarArchiveEntry("d");
66 tarEntry.setModTime(modificationDate);
67 tarEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
68 tarEntry.setModTime(modificationDate.getTime());
69 tarEntry.setName(fileName);
70 outputStream.putArchiveEntry(tarEntry);
71 outputStream.closeArchiveEntry();
72 }
73 assertTrue(ref.isClosed());
74 return baos.toByteArray();
75 }
76
77 private byte[] getResourceContents(final String name) throws IOException {
78 final ByteArrayOutputStream bos;
79 try (InputStream resourceAsStream = getClass().getResourceAsStream(name)) {
80 bos = new ByteArrayOutputStream();
81 IOUtils.copy(resourceAsStream, bos);
82 }
83 return bos.toByteArray();
84 }
85
86 @Test
87 void testBigNumberErrorMode() throws Exception {
88 final TarArchiveEntry t = new TarArchiveEntry("foo");
89 t.setSize(0100000000000L);
90 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
91 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
92 assertThrows(IllegalArgumentException.class, () -> tos.putArchiveEntry(t));
93 }
94 }
95
96 @Test
97 void testBigNumberPosixMode() throws Exception {
98 final TarArchiveEntry t = new TarArchiveEntry("foo");
99 t.setSize(0100000000000L);
100 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
101 final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
102 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
103 tos.putArchiveEntry(t);
104
105 tos.write(new byte[10 * 1024]);
106 final byte[] data = bos.toByteArray();
107 assertEquals("00000000000 ",
108 new String(data, 1024 + TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, UTF_8));
109 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
110 final TarArchiveEntry e = tin.getNextTarEntry();
111 assertEquals(0100000000000L, e.getSize());
112 }
113
114
115 closeQuietly(tos);
116 }
117
118 @Test
119 void testBigNumberStarMode() throws Exception {
120 final TarArchiveEntry t = new TarArchiveEntry("foo");
121 t.setSize(0100000000000L);
122 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
123 final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
124 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
125 tos.putArchiveEntry(t);
126
127 tos.write(new byte[10 * 1024]);
128 final byte[] data = bos.toByteArray();
129 assertEquals(0x80, data[TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN] & 0x80);
130 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
131 final TarArchiveEntry e = tin.getNextTarEntry();
132 assertEquals(0100000000000L, e.getSize());
133 }
134
135
136 closeQuietly(tos);
137 }
138
139 @Test
140 void testBlockSizes() throws Exception {
141 final String fileName = "/test1.xml";
142 final byte[] contents = getResourceContents(fileName);
143 testPadding(TarConstants.DEFAULT_BLKSIZE, fileName, contents);
144 testPadding(5120, fileName, contents);
145 testPadding(1 << 15, fileName, contents);
146 testPadding(-2, fileName, contents);
147
148
149 assertThrows(IllegalArgumentException.class, () -> testPadding(511, fileName, contents));
150
151
152 assertThrows(IllegalArgumentException.class, () -> testPadding(0, fileName, contents));
153
154
155 final byte[] contents2 = ArrayFill.fill(new byte[2048], (byte) 42);
156 testPadding(TarConstants.DEFAULT_BLKSIZE, fileName, contents2);
157 }
158
159 @Test
160 void testCount() throws Exception {
161 final File f = createTempFile("commons-compress-tarcount", ".tar");
162 try (OutputStream fos = Files.newOutputStream(f.toPath());
163 ArchiveOutputStream<ArchiveEntry> tarOut = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream(ArchiveStreamFactory.TAR, fos)) {
164 final File file1 = getFile("test1.xml");
165 final TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName());
166 tarOut.putArchiveEntry(sEntry);
167 try (InputStream in = Files.newInputStream(file1.toPath())) {
168 final byte[] buf = new byte[8192];
169 int read = 0;
170 while ((read = in.read(buf)) > 0) {
171 tarOut.write(buf, 0, read);
172 }
173 }
174 tarOut.closeArchiveEntry();
175
176 tarOut.close();
177 assertEquals(f.length(), tarOut.getBytesWritten());
178 }
179 }
180
181
182
183
184
185 @Test
186 void testLongNameMd5Hash() throws Exception {
187
188 final String longFileName =
189 "a/considerably/longer/file/name/which/forces/use/of/the/long/link/header/which/appears/to/always/use/the/current/time/as/modification/date";
190
191 final Date modificationDate = new Date();
192
193 final byte[] archive1 = createTarArchiveContainingOneDirectory(longFileName, modificationDate);
194 final byte[] digest1 = MessageDigest.getInstance("MD5").digest(archive1);
195
196
197 Thread.sleep(1000L);
198
199
200 final byte[] archive2 = createTarArchiveContainingOneDirectory(longFileName, modificationDate);
201
202 final byte[] digest2 = MessageDigest.getInstance("MD5").digest(archive2);
203
204 assertArrayEquals(digest1, digest2);
205
206
207
208 Thread.sleep(1000);
209 try (TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(archive2))) {
210 final ArchiveEntry nextEntry = tarIn.getNextEntry();
211 assertEquals(longFileName, nextEntry.getName());
212
213 assertEquals(modificationDate.getTime() / 1000, nextEntry.getLastModifiedDate().getTime() / 1000);
214 }
215 }
216
217 @Test
218 void testMaxFileSizeError() throws Exception {
219 final TarArchiveEntry t = new TarArchiveEntry("foo");
220 t.setSize(077777777777L);
221 final TarArchiveOutputStream tos1 = new TarArchiveOutputStream(new ByteArrayOutputStream());
222 tos1.putArchiveEntry(t);
223 t.setSize(0100000000000L);
224 final TarArchiveOutputStream tos2 = new TarArchiveOutputStream(new ByteArrayOutputStream());
225 assertThrows(RuntimeException.class, () -> tos2.putArchiveEntry(t), "Should have generated RuntimeException");
226 }
227
228 @Test
229 void testOldEntryError() throws Exception {
230 final TarArchiveEntry t = new TarArchiveEntry("foo");
231 t.setSize(Integer.MAX_VALUE);
232 t.setModTime(-1000);
233 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream())) {
234 assertThrows(RuntimeException.class, () -> tos.putArchiveEntry(t));
235 }
236 }
237
238 @Test
239 void testOldEntryPosixMode() throws Exception {
240 final TarArchiveEntry t = new TarArchiveEntry("foo");
241 t.setSize(Integer.MAX_VALUE);
242 t.setModTime(-1000);
243 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
244 final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
245 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
246 tos.putArchiveEntry(t);
247
248 tos.write(new byte[10 * 1024]);
249 final byte[] data = bos.toByteArray();
250 assertEquals("00000000000 ", new String(data,
251 1024 + TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN + TarConstants.SIZELEN, 12, UTF_8));
252 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
253 final TarArchiveEntry e = tin.getNextTarEntry();
254 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
255 cal.set(1969, 11, 31, 23, 59, 59);
256 cal.set(Calendar.MILLISECOND, 0);
257 assertEquals(cal.getTime(), e.getLastModifiedDate());
258 }
259
260
261 closeQuietly(tos);
262 }
263
264 @Test
265 void testOldEntryStarMode() throws Exception {
266 final TarArchiveEntry t = new TarArchiveEntry("foo");
267 t.setSize(Integer.MAX_VALUE);
268 t.setModTime(-1000);
269 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
270 final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
271 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
272 tos.putArchiveEntry(t);
273
274 tos.write(new byte[10 * 1024]);
275 final byte[] data = bos.toByteArray();
276 assertEquals((byte) 0xff, data[TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN + TarConstants.SIZELEN]);
277 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
278 final TarArchiveEntry e = tin.getNextTarEntry();
279 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
280 cal.set(1969, 11, 31, 23, 59, 59);
281 cal.set(Calendar.MILLISECOND, 0);
282 assertEquals(cal.getTime(), e.getLastModifiedDate());
283 }
284
285
286 closeQuietly(tos);
287 }
288
289 private void testPadding(int blockSize, final String fileName, final byte[] contents) throws IOException {
290 final File f = createTempFile("commons-compress-padding", ".tar");
291 try (OutputStream fos = Files.newOutputStream(f.toPath())) {
292 final TarArchiveOutputStream tos;
293 if (blockSize != -2) {
294 tos = new TarArchiveOutputStream(fos, blockSize);
295 } else {
296 blockSize = 512;
297 tos = new TarArchiveOutputStream(fos);
298 }
299 final TarArchiveEntry sEntry;
300 sEntry = new TarArchiveEntry(fileName);
301 sEntry.setSize(contents.length);
302 tos.putArchiveEntry(sEntry);
303 tos.write(contents);
304 tos.closeArchiveEntry();
305 tos.close();
306 final int fileRecordsSize = (int) Math.ceil((double) contents.length / 512) * 512;
307 final int headerSize = 512;
308 final int endOfArchiveSize = 1024;
309 final int unpaddedSize = headerSize + fileRecordsSize + endOfArchiveSize;
310 final int paddedSize = (int) Math.ceil((double) unpaddedSize / blockSize) * blockSize;
311 assertEquals(paddedSize, f.length());
312 }
313 }
314
315 @Test
316 void testPaxHeadersWithLength101() throws Exception {
317 final Map<String, String> m = new HashMap<>();
318 m.put("a", "0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123");
319 final byte[] data = writePaxHeader(m);
320 assertEquals("00000000145 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, UTF_8));
321 assertEquals("101 a=0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123\n",
322 new String(data, 512, 101, UTF_8));
323 }
324
325 @Test
326 void testPaxHeadersWithLength99() throws Exception {
327 final Map<String, String> m = new HashMap<>();
328 m.put("a", "0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012");
329 final byte[] data = writePaxHeader(m);
330 assertEquals("00000000143 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, UTF_8));
331 assertEquals("99 a=0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012\n",
332 new String(data, 512, 99, UTF_8));
333 }
334
335 @Test
336 void testPutGlobalPaxHeaderEntry() throws IOException {
337 final String x = "If at first you don't succeed, give up";
338 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
339 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
340 final int pid = 73;
341 final int globCount = 1;
342 final byte lfPaxGlobalExtendedHeader = TarConstants.LF_PAX_GLOBAL_EXTENDED_HEADER;
343 final TarArchiveEntry globalHeader = new TarArchiveEntry("/tmp/GlobalHead." + pid + "." + globCount, lfPaxGlobalExtendedHeader);
344 globalHeader.addPaxHeader("SCHILLY.xattr.user.org.apache.weasels", "global-weasels");
345 tos.putArchiveEntry(globalHeader);
346 TarArchiveEntry entry = new TarArchiveEntry("message");
347 entry.setSize(x.length());
348 tos.putArchiveEntry(entry);
349 tos.write(x.getBytes());
350 tos.closeArchiveEntry();
351 entry = new TarArchiveEntry("counter-message");
352 final String y = "Nothing succeeds like excess";
353 entry.setSize(y.length());
354 entry.addPaxHeader("SCHILLY.xattr.user.org.apache.weasels.species", "unknown");
355 tos.putArchiveEntry(entry);
356 tos.write(y.getBytes());
357 tos.closeArchiveEntry();
358 }
359 final TarArchiveInputStream in = new TarArchiveInputStream(new ByteArrayInputStream(bos.toByteArray()));
360 TarArchiveEntry entryIn = in.getNextTarEntry();
361 assertNotNull(entryIn);
362 assertEquals("message", entryIn.getName());
363 assertEquals(TarConstants.LF_NORMAL, entryIn.getLinkFlag());
364 assertEquals("global-weasels", entryIn.getExtraPaxHeader("SCHILLY.xattr.user.org.apache.weasels"));
365 final Reader reader = new InputStreamReader(in);
366 for (int i = 0; i < x.length(); i++) {
367 assertEquals(x.charAt(i), reader.read());
368 }
369 assertEquals(-1, reader.read());
370 entryIn = in.getNextTarEntry();
371 assertEquals("counter-message", entryIn.getName());
372 assertEquals("global-weasels", entryIn.getExtraPaxHeader("SCHILLY.xattr.user.org.apache.weasels"));
373 assertEquals("unknown", entryIn.getExtraPaxHeader("SCHILLY.xattr.user.org.apache.weasels.species"));
374 assertNull(in.getNextTarEntry());
375 }
376
377 @SuppressWarnings("deprecation")
378 @Test
379 void testRecordSize() throws IOException {
380 assertThrows(IllegalArgumentException.class, () -> new TarArchiveOutputStream(new ByteArrayOutputStream(), 512, 511),
381 "should have rejected recordSize of 511");
382 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream(), 512, 512)) {
383 assertEquals(512, tos.getRecordSize(), "recordSize");
384 }
385 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream(), 512, 512, null)) {
386 assertEquals(512, tos.getRecordSize(), "recordSize");
387 }
388 }
389
390 private void testRoundtripWith67CharFileName(final int mode) throws Exception {
391 final String n = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
392 assertEquals(67, n.length());
393 final TarArchiveEntry t = new TarArchiveEntry(n);
394 t.setSize(10 * 1024);
395 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
396 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
397 tos.setLongFileMode(mode);
398 tos.putArchiveEntry(t);
399 tos.write(new byte[10 * 1024]);
400 tos.closeArchiveEntry();
401 }
402 final byte[] data = bos.toByteArray();
403 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
404 assertEquals(n, tin.getNextTarEntry().getName());
405 }
406 }
407
408
409
410
411 @Test
412 void testRoundtripWith67CharFileNameGnu() throws Exception {
413 testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_GNU);
414 }
415
416
417
418
419 @Test
420 void testRoundtripWith67CharFileNamePosix() throws Exception {
421 testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_POSIX);
422 }
423
424 private void testWriteLongDirectoryName(final int mode) throws Exception {
425 final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
426 + "01234567890123456789012345678901234567890123456789/";
427 final TarArchiveEntry t = new TarArchiveEntry(n);
428 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
429 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
430 tos.setLongFileMode(mode);
431 tos.putArchiveEntry(t);
432 tos.closeArchiveEntry();
433 }
434 final byte[] data = bos.toByteArray();
435 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
436 final TarArchiveEntry e = tin.getNextTarEntry();
437 assertEquals(n, e.getName());
438 assertTrue(e.isDirectory());
439 }
440 }
441
442 @Test
443 void testWriteLongDirectoryNameErrorMode() throws Exception {
444 final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
445 + "01234567890123456789012345678901234567890123456789/";
446
447 assertThrows(RuntimeException.class, () -> {
448 final TarArchiveEntry t = new TarArchiveEntry(n);
449 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
450 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
451 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR);
452 tos.putArchiveEntry(t);
453 tos.closeArchiveEntry();
454 }
455 }, "Truncated name didn't throw an exception");
456 }
457
458
459
460
461 @Test
462 void testWriteLongDirectoryNameGnuMode() throws Exception {
463 testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_GNU);
464 }
465
466
467
468
469 @Test
470 void testWriteLongDirectoryNamePosixMode() throws Exception {
471 testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_POSIX);
472 }
473
474 @Test
475 void testWriteLongDirectoryNameTruncateMode() throws Exception {
476 final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
477 + "01234567890123456789012345678901234567890123456789/";
478 final TarArchiveEntry t = new TarArchiveEntry(n);
479 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
480 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
481 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE);
482 tos.putArchiveEntry(t);
483 tos.closeArchiveEntry();
484 }
485 final byte[] data = bos.toByteArray();
486 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
487 final TarArchiveEntry e = tin.getNextTarEntry();
488 assertEquals(n.substring(0, TarConstants.NAMELEN) + "/", e.getName(), "Entry name");
489 assertEquals(TarConstants.LF_DIR, e.getLinkFlag());
490 assertTrue(e.isDirectory(), "The entry is not a directory");
491 }
492 }
493
494 @Test
495 void testWriteLongFileNamePosixMode() throws Exception {
496
497 final String n = "01234567890123456789012345678901234567890123456789"
498 + "01234567890123456789012345678901234567890123456789"
499 + "01234567890123456789012345678901234567890123456789";
500
501 final TarArchiveEntry t = new TarArchiveEntry(n);
502 t.setSize(10 * 1024);
503 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
504 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
505 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
506 tos.putArchiveEntry(t);
507 tos.write(new byte[10 * 1024]);
508 tos.closeArchiveEntry();
509 final byte[] data = bos.toByteArray();
510 assertEquals("160 path=" + n + "\n", new String(data, 512, 160, UTF_8));
511 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
512 assertEquals(n, tin.getNextTarEntry().getName());
513 assertEquals(TarConstants.LF_NORMAL, tin.getCurrentEntry().getLinkFlag());
514 }
515 }
516 }
517
518 @Test
519 void testWriteLongFileNameThrowsException() throws Exception {
520 final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
521 + "01234567890123456789012345678901234567890123456789";
522 final TarArchiveEntry t = new TarArchiveEntry(n);
523 final TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream(), "ASCII");
524 assertThrows(IllegalArgumentException.class, () -> tos.putArchiveEntry(t));
525 }
526
527
528
529
530 private void testWriteLongLinkName(final int mode) throws Exception {
531 final String linkName = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
532 + "01234567890123456789012345678901234567890123456789/test";
533 final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
534 entry.setLinkName(linkName);
535
536 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
537 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
538 tos.setLongFileMode(mode);
539 tos.putArchiveEntry(entry);
540 tos.closeArchiveEntry();
541 }
542
543 final byte[] data = bos.toByteArray();
544 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
545 final TarArchiveEntry e = tin.getNextTarEntry();
546 assertEquals("test", e.getName(), "Entry name");
547 assertEquals(linkName, e.getLinkName(), "Link name");
548 assertTrue(e.isSymbolicLink(), "The entry is not a symbolic link");
549 assertEquals(TarConstants.LF_SYMLINK, e.getLinkFlag(), "Link flag");
550 }
551 }
552
553
554
555
556 @Test
557 void testWriteLongLinkNameErrorMode() throws Exception {
558 final String linkName = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
559 + "01234567890123456789012345678901234567890123456789/test";
560 final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
561 entry.setLinkName(linkName);
562
563 assertThrows(RuntimeException.class, () -> {
564 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
565 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
566 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR);
567 tos.putArchiveEntry(entry);
568 tos.closeArchiveEntry();
569 }
570 }, "Truncated link name didn't throw an exception");
571 }
572
573
574
575
576 @Test
577 void testWriteLongLinkNameGnuMode() throws Exception {
578 testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_GNU);
579 }
580
581
582
583
584 @Test
585 void testWriteLongLinkNamePosixMode() throws Exception {
586 testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_POSIX);
587 }
588
589 @Test
590 void testWriteLongLinkNameTruncateMode() throws Exception {
591 final String linkName = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"
592 + "01234567890123456789012345678901234567890123456789/";
593 final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
594 entry.setLinkName(linkName);
595
596 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
597 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
598 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE);
599 tos.putArchiveEntry(entry);
600 tos.closeArchiveEntry();
601 }
602
603 final byte[] data = bos.toByteArray();
604 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
605 final TarArchiveEntry e = tin.getNextTarEntry();
606 assertEquals(linkName.substring(0, TarConstants.NAMELEN), e.getLinkName(), "Link name");
607 assertEquals(TarConstants.LF_SYMLINK, e.getLinkFlag(), "Link flag");
608 }
609 }
610
611
612
613
614 @Test
615 void testWriteNonAsciiDirectoryNamePosixMode() throws Exception {
616 final String n = "f\u00f6\u00f6/";
617 final TarArchiveEntry t = new TarArchiveEntry(n);
618 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
619 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
620 tos.setAddPaxHeadersForNonAsciiNames(true);
621 tos.putArchiveEntry(t);
622 tos.closeArchiveEntry();
623 }
624 final byte[] data = bos.toByteArray();
625 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
626 final TarArchiveEntry e = tin.getNextTarEntry();
627 assertEquals(n, e.getName());
628 assertEquals(TarConstants.LF_DIR, e.getLinkFlag());
629 assertTrue(e.isDirectory());
630 }
631 }
632
633 @Test
634 void testWriteNonAsciiLinkPathNamePaxHeader() throws Exception {
635 final String n = "\u00e4";
636 final TarArchiveEntry t = new TarArchiveEntry("a", TarConstants.LF_LINK);
637 t.setSize(10 * 1024);
638 t.setLinkName(n);
639 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
640 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
641 tos.setAddPaxHeadersForNonAsciiNames(true);
642 tos.putArchiveEntry(t);
643 tos.write(new byte[10 * 1024]);
644 tos.closeArchiveEntry();
645 }
646 final byte[] data = bos.toByteArray();
647 assertEquals("15 linkpath=" + n + "\n", new String(data, 512, 15, UTF_8));
648 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
649 final TarArchiveEntry e = tin.getNextTarEntry();
650 assertEquals(n, e.getLinkName());
651 assertEquals(TarConstants.LF_LINK, e.getLinkFlag(), "Link flag");
652 }
653 }
654
655
656
657
658 @Test
659 void testWriteNonAsciiNameWithUnfortunateNamePosixMode() throws Exception {
660 final String n = "f\u00f6\u00f6\u00dc";
661 final TarArchiveEntry t = new TarArchiveEntry(n);
662 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
663 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
664 tos.setAddPaxHeadersForNonAsciiNames(true);
665 tos.putArchiveEntry(t);
666 tos.closeArchiveEntry();
667 }
668 final byte[] data = bos.toByteArray();
669 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
670 final TarArchiveEntry e = tin.getNextTarEntry();
671 assertEquals(n, e.getName());
672 assertEquals(TarConstants.LF_NORMAL, e.getLinkFlag());
673 assertFalse(e.isDirectory());
674 }
675 }
676
677 @Test
678 void testWriteNonAsciiPathNamePaxHeader() throws Exception {
679 final String n = "\u00e4";
680 final TarArchiveEntry t = new TarArchiveEntry(n);
681 t.setSize(10 * 1024);
682 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
683 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
684 tos.setAddPaxHeadersForNonAsciiNames(true);
685 tos.putArchiveEntry(t);
686 tos.write(new byte[10 * 1024]);
687 tos.closeArchiveEntry();
688 }
689 final byte[] data = bos.toByteArray();
690 assertEquals("11 path=" + n + "\n", new String(data, 512, 11, UTF_8));
691 try (TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data))) {
692 final TarArchiveEntry e = tin.getNextTarEntry();
693 assertEquals(n, e.getName());
694 assertEquals(TarConstants.LF_NORMAL, e.getLinkFlag());
695 }
696 }
697
698 @Test
699 void testWriteSimplePaxHeaders() throws Exception {
700 final Map<String, String> m = new HashMap<>();
701 m.put("a", "b");
702 final byte[] data = writePaxHeader(m);
703 assertEquals("00000000006 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, UTF_8));
704 assertEquals("6 a=b\n", new String(data, 512, 6, UTF_8));
705 }
706
707
708
709
710 @Disabled("The test needs to write 1.1 TB in chunks of 512 bytes which takes a long time. So it's disabled by default")
711 @Test
712 void testWritingBigFile() throws Exception {
713 final TarArchiveEntry t = new TarArchiveEntry("foo");
714 t.setSize((Integer.MAX_VALUE + 1L) * TarConstants.DEFAULT_RCDSIZE);
715 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(NullOutputStream.INSTANCE)) {
716 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
717 tos.putArchiveEntry(t);
718
719 final byte[] bytes = new byte[TarConstants.DEFAULT_RCDSIZE];
720 for (int i = 0; i < Integer.MAX_VALUE; i++) {
721 tos.write(bytes);
722 }
723 tos.write(bytes);
724 tos.closeArchiveEntry();
725 }
726 }
727
728 private byte[] writePaxHeader(final Map<String, String> m) throws Exception {
729 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
730 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII")) {
731 tos.writePaxHeaders(new TarArchiveEntry("x"), "foo", m);
732
733
734 final TarArchiveEntry t = new TarArchiveEntry("foo");
735 t.setSize(10 * 1024);
736 tos.putArchiveEntry(t);
737 tos.write(new byte[10 * 1024]);
738 tos.closeArchiveEntry();
739 }
740
741 return bos.toByteArray();
742 }
743
744 }