/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.commons.compress.archivers.tar; import static org.junit.Assert.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import org.apache.commons.compress.AbstractTestCase; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.utils.CharsetNames; import org.apache.commons.compress.utils.IOUtils; import org.junit.Assert; import org.junit.Test; public class TarArchiveOutputStreamTest extends AbstractTestCase { @Test public void testCount() throws Exception { final File f = File.createTempFile("commons-compress-tarcount", ".tar"); f.deleteOnExit(); final FileOutputStream fos = new FileOutputStream(f); final ArchiveOutputStream tarOut = new ArchiveStreamFactory() .createArchiveOutputStream(ArchiveStreamFactory.TAR, fos); final File file1 = getFile("test1.xml"); final TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName()); tarOut.putArchiveEntry(sEntry); final FileInputStream in = new FileInputStream(file1); final byte[] buf = new byte[8192]; int read = 0; while ((read = in.read(buf)) > 0) { tarOut.write(buf, 0, read); } in.close(); tarOut.closeArchiveEntry(); tarOut.close(); assertEquals(f.length(), tarOut.getBytesWritten()); } @Test public void testMaxFileSizeError() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(077777777777L); TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream()); tos.putArchiveEntry(t); t.setSize(0100000000000L); tos = new TarArchiveOutputStream(new ByteArrayOutputStream()); try { tos.putArchiveEntry(t); fail("Should have generated RuntimeException"); } catch (final RuntimeException expected) { } } @Test public void testBigNumberStarMode() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(0100000000000L); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR); tos.putArchiveEntry(t); // make sure header is written to byte array tos.write(new byte[10 * 1024]); final byte[] data = bos.toByteArray(); assertEquals(0x80, data[TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN] & 0x80); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(0100000000000L, e.getSize()); tin.close(); // generates IOE because of unclosed entries. // However we don't really want to create such large entries. closeQuietly(tos); } @Test public void testBigNumberPosixMode() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(0100000000000L); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); tos.putArchiveEntry(t); // make sure header is written to byte array tos.write(new byte[10 * 1024]); final byte[] data = bos.toByteArray(); assertEquals("00000000000 ", new String(data, 1024 + TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, CharsetNames.UTF_8)); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(0100000000000L, e.getSize()); tin.close(); // generates IOE because of unclosed entries. // However we don't really want to create such large entries. closeQuietly(tos); } @Test public void testWriteSimplePaxHeaders() throws Exception { final Map<String, String> m = new HashMap<>(); m.put("a", "b"); final byte[] data = writePaxHeader(m); assertEquals("00000000006 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, CharsetNames.UTF_8)); assertEquals("6 a=b\n", new String(data, 512, 6, CharsetNames.UTF_8)); } @Test public void testPaxHeadersWithLength99() throws Exception { final Map<String, String> m = new HashMap<>(); m.put("a", "0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012"); final byte[] data = writePaxHeader(m); assertEquals("00000000143 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, CharsetNames.UTF_8)); assertEquals("99 a=0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "012\n", new String(data, 512, 99, CharsetNames.UTF_8)); } @Test public void testPaxHeadersWithLength101() throws Exception { final Map<String, String> m = new HashMap<>(); m.put("a", "0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123"); final byte[] data = writePaxHeader(m); assertEquals("00000000145 ", new String(data, TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN, 12, CharsetNames.UTF_8)); assertEquals("101 a=0123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "0123\n", new String(data, 512, 101, CharsetNames.UTF_8)); } private byte[] writePaxHeader(final Map<String, String> m) throws Exception { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.writePaxHeaders(new TarArchiveEntry("x"), "foo", m); // add a dummy entry so data gets written final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(10 * 1024); tos.putArchiveEntry(t); tos.write(new byte[10 * 1024]); tos.closeArchiveEntry(); tos.close(); return bos.toByteArray(); } @Test public void testWriteLongFileNamePosixMode() throws Exception { final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"; final TarArchiveEntry t = new TarArchiveEntry(n); t.setSize(10 * 1024); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); tos.putArchiveEntry(t); tos.write(new byte[10 * 1024]); tos.closeArchiveEntry(); final byte[] data = bos.toByteArray(); assertEquals("160 path=" + n + "\n", new String(data, 512, 160, CharsetNames.UTF_8)); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); tin.close(); tos.close(); } @Test public void testOldEntryStarMode() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(Integer.MAX_VALUE); t.setModTime(-1000); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR); tos.putArchiveEntry(t); // make sure header is written to byte array tos.write(new byte[10 * 1024]); final byte[] data = bos.toByteArray(); assertEquals((byte) 0xff, data[TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN + TarConstants.SIZELEN]); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); cal.set(1969, 11, 31, 23, 59, 59); cal.set(Calendar.MILLISECOND, 0); assertEquals(cal.getTime(), e.getLastModifiedDate()); tin.close(); // generates IOE because of unclosed entries. // However we don't really want to create such large entries. closeQuietly(tos); } @Test public void testOldEntryPosixMode() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(Integer.MAX_VALUE); t.setModTime(-1000); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); tos.putArchiveEntry(t); // make sure header is written to byte array tos.write(new byte[10 * 1024]); final byte[] data = bos.toByteArray(); assertEquals("00000000000 ", new String(data, 1024 + TarConstants.NAMELEN + TarConstants.MODELEN + TarConstants.UIDLEN + TarConstants.GIDLEN + TarConstants.SIZELEN, 12, CharsetNames.UTF_8)); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); cal.set(1969, 11, 31, 23, 59, 59); cal.set(Calendar.MILLISECOND, 0); assertEquals(cal.getTime(), e.getLastModifiedDate()); tin.close(); // generates IOE because of unclosed entries. // However we don't really want to create such large entries. closeQuietly(tos); } @Test public void testOldEntryError() throws Exception { final TarArchiveEntry t = new TarArchiveEntry("foo"); t.setSize(Integer.MAX_VALUE); t.setModTime(-1000); final TarArchiveOutputStream tos = new TarArchiveOutputStream(new ByteArrayOutputStream()); try { tos.putArchiveEntry(t); fail("Should have generated RuntimeException"); } catch (final RuntimeException expected) { } tos.close(); } @Test public void testWriteNonAsciiPathNamePaxHeader() throws Exception { final String n = "\u00e4"; final TarArchiveEntry t = new TarArchiveEntry(n); t.setSize(10 * 1024); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setAddPaxHeadersForNonAsciiNames(true); tos.putArchiveEntry(t); tos.write(new byte[10 * 1024]); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); assertEquals("11 path=" + n + "\n", new String(data, 512, 11, CharsetNames.UTF_8)); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); tin.close(); } @Test public void testWriteNonAsciiLinkPathNamePaxHeader() throws Exception { final String n = "\u00e4"; final TarArchiveEntry t = new TarArchiveEntry("a", TarConstants.LF_LINK); t.setSize(10 * 1024); t.setLinkName(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setAddPaxHeadersForNonAsciiNames(true); tos.putArchiveEntry(t); tos.write(new byte[10 * 1024]); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); assertEquals("15 linkpath=" + n + "\n", new String(data, 512, 15, CharsetNames.UTF_8)); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getLinkName()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-200" */ @Test public void testRoundtripWith67CharFileNameGnu() throws Exception { testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_GNU); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-200" */ @Test public void testRoundtripWith67CharFileNamePosix() throws Exception { testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_POSIX); } private void testRoundtripWith67CharFileName(final int mode) throws Exception { final String n = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAA"; assertEquals(67, n.length()); final TarArchiveEntry t = new TarArchiveEntry(n); t.setSize(10 * 1024); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(mode); tos.putArchiveEntry(t); tos.write(new byte[10 * 1024]); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); tin.close(); } @Test public void testWriteLongDirectoryNameErrorMode() throws Exception { final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/"; try { final TarArchiveEntry t = new TarArchiveEntry(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR); tos.putArchiveEntry(t); tos.closeArchiveEntry(); tos.close(); fail("Truncated name didn't throw an exception"); } catch (final RuntimeException e) { // expected } } @Test public void testWriteLongDirectoryNameTruncateMode() throws Exception { final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/"; final TarArchiveEntry t = new TarArchiveEntry(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE); tos.putArchiveEntry(t); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals("Entry name", n.substring(0, TarConstants.NAMELEN) + "/", e.getName()); assertTrue("The entry is not a directory", e.isDirectory()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-203" */ @Test public void testWriteLongDirectoryNameGnuMode() throws Exception { testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_GNU); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-203" */ @Test public void testWriteLongDirectoryNamePosixMode() throws Exception { testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_POSIX); } private void testWriteLongDirectoryName(final int mode) throws Exception { final String n = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/"; final TarArchiveEntry t = new TarArchiveEntry(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(mode); tos.putArchiveEntry(t); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); assertTrue(e.isDirectory()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-203" */ @Test public void testWriteNonAsciiDirectoryNamePosixMode() throws Exception { final String n = "f\u00f6\u00f6/"; final TarArchiveEntry t = new TarArchiveEntry(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setAddPaxHeadersForNonAsciiNames(true); tos.putArchiveEntry(t); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); assertTrue(e.isDirectory()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-265" */ @Test public void testWriteNonAsciiNameWithUnfortunateNamePosixMode() throws Exception { final String n = "f\u00f6\u00f6\u00dc"; final TarArchiveEntry t = new TarArchiveEntry(n); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); tos.setAddPaxHeadersForNonAsciiNames(true); tos.putArchiveEntry(t); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals(n, e.getName()); assertFalse(e.isDirectory()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-237" */ @Test public void testWriteLongLinkNameErrorMode() throws Exception { final String linkname = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/test"; final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK); entry.setLinkName(linkname); try { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR); tos.putArchiveEntry(entry); tos.closeArchiveEntry(); tos.close(); fail("Truncated link name didn't throw an exception"); } catch (final RuntimeException e) { // expected } } @Test public void testWriteLongLinkNameTruncateMode() throws Exception { final String linkname = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/"; final TarArchiveEntry entry = new TarArchiveEntry("test" , TarConstants.LF_SYMLINK); entry.setLinkName(linkname); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE); tos.putArchiveEntry(entry); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals("Link name", linkname.substring(0, TarConstants.NAMELEN), e.getLinkName()); tin.close(); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-237" */ @Test public void testWriteLongLinkNameGnuMode() throws Exception { testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_GNU); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-237" */ @Test public void testWriteLongLinkNamePosixMode() throws Exception { testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_POSIX); } /** * @see "https://issues.apache.org/jira/browse/COMPRESS-237" */ private void testWriteLongLinkName(final int mode) throws Exception { final String linkname = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789/test"; final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK); entry.setLinkName(linkname); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII"); tos.setLongFileMode(mode); tos.putArchiveEntry(entry); tos.closeArchiveEntry(); tos.close(); final byte[] data = bos.toByteArray(); final TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data)); final TarArchiveEntry e = tin.getNextTarEntry(); assertEquals("Entry name", "test", e.getName()); assertEquals("Link name", linkname, e.getLinkName()); assertTrue("The entry is not a symbolic link", e.isSymbolicLink()); tin.close(); } @Test public void testPadsOutputToFullBlockLength() throws Exception { final File f = File.createTempFile("commons-compress-padding", ".tar"); f.deleteOnExit(); final FileOutputStream fos = new FileOutputStream(f); final TarArchiveOutputStream tos = new TarArchiveOutputStream(fos); final File file1 = getFile("test1.xml"); final TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName()); tos.putArchiveEntry(sEntry); final FileInputStream in = new FileInputStream(file1); IOUtils.copy(in, tos); in.close(); tos.closeArchiveEntry(); tos.close(); // test1.xml is small enough to fit into the default block size assertEquals(TarConstants.DEFAULT_BLKSIZE, f.length()); } /** * When using long file names the longLinkEntry included the * current timestamp as the Entry modification date. This was * never exposed to the client but it caused identical archives to * have different MD5 hashes. * * @throws Exception */ @Test public void testLongNameMd5Hash() throws Exception { final String longFileName = "a/considerably/longer/file/name/which/forces/use/of/the/long/link/header/which/appears/to/always/use/the/current/time/as/modification/date"; final String fname = longFileName; final Date modificationDate = new Date(); final byte[] archive1 = createTarArchiveContainingOneDirectory(fname, modificationDate); final byte[] digest1 = MessageDigest.getInstance("MD5").digest(archive1); // let a second elapse otherwise the modification dates will be equal Thread.sleep(1000L); // now recreate exactly the same tar file final byte[] archive2 = createTarArchiveContainingOneDirectory(fname, modificationDate); // and I would expect the MD5 hash to be the same, but for long names it isn't final byte[] digest2 = MessageDigest.getInstance("MD5").digest(archive2); Assert.assertArrayEquals(digest1, digest2); // do I still have the correct modification date? // let a second elapse so we don't get the current time Thread.sleep(1000); final TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(archive2)); final ArchiveEntry nextEntry = tarIn.getNextEntry(); assertEquals(longFileName, nextEntry.getName()); // tar archive stores modification time to second granularity only (floored) assertEquals(modificationDate.getTime() / 1000, nextEntry.getLastModifiedDate().getTime() / 1000); tarIn.close(); } private static byte[] createTarArchiveContainingOneDirectory(final String fname, final Date modificationDate) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final TarArchiveOutputStream tarOut = new TarArchiveOutputStream(baos, 1024); tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); final TarArchiveEntry tarEntry = new TarArchiveEntry("d"); tarEntry.setModTime(modificationDate); tarEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE); tarEntry.setModTime(modificationDate.getTime()); tarEntry.setName(fname); tarOut.putArchiveEntry(tarEntry); tarOut.closeArchiveEntry(); tarOut.close(); return baos.toByteArray(); } }