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