156276d05SJonathan Nieder /* 2*5c5f7c6bSMatthias Sohn * Copyright (C) 2012 Google Inc. and others 356276d05SJonathan Nieder * 4*5c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 5*5c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 6*5c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 756276d05SJonathan Nieder * 8*5c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 956276d05SJonathan Nieder */ 1056276d05SJonathan Nieder package org.eclipse.jgit.archive; 1156276d05SJonathan Nieder 12fbf6ce65SDavid Pursehouse import static java.nio.charset.StandardCharsets.UTF_8; 13f07b6023SDavid Pursehouse 1456276d05SJonathan Nieder import java.io.IOException; 1556276d05SJonathan Nieder import java.io.OutputStream; 16c0c4c6f0SDavid Ostrovsky import java.text.MessageFormat; 1756cb2d92SJonathan Nieder import java.util.Arrays; 1856cb2d92SJonathan Nieder import java.util.Collections; 1956cb2d92SJonathan Nieder import java.util.List; 20c0c4c6f0SDavid Ostrovsky import java.util.Map; 2156276d05SJonathan Nieder 2256276d05SJonathan Nieder import org.apache.commons.compress.archivers.ArchiveOutputStream; 2356276d05SJonathan Nieder import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 2456276d05SJonathan Nieder import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 2556276d05SJonathan Nieder import org.apache.commons.compress.archivers.tar.TarConstants; 2656276d05SJonathan Nieder import org.eclipse.jgit.api.ArchiveCommand; 272ecc27dbSJonathan Nieder import org.eclipse.jgit.archive.internal.ArchiveText; 2856276d05SJonathan Nieder import org.eclipse.jgit.lib.FileMode; 291448ec37SNaoki Takezoe import org.eclipse.jgit.lib.ObjectId; 3056276d05SJonathan Nieder import org.eclipse.jgit.lib.ObjectLoader; 31b2610246SYasuhiro Takagi import org.eclipse.jgit.revwalk.RevCommit; 32b2610246SYasuhiro Takagi 3356276d05SJonathan Nieder 34659cadf0SJonathan Nieder /** 35ebfe85d0SJonathan Nieder * Unix TAR format (ustar + some PAX extensions). 36659cadf0SJonathan Nieder */ 37c0c4c6f0SDavid Ostrovsky public final class TarFormat extends BaseFormat implements 38c0c4c6f0SDavid Ostrovsky ArchiveCommand.Format<ArchiveOutputStream> { 394ceb25b6SRobin Rosenberg private static final List<String> SUFFIXES = Collections 404ceb25b6SRobin Rosenberg .unmodifiableList(Arrays.asList(".tar")); //$NON-NLS-1$ 4156cb2d92SJonathan Nieder 4232022e97SMatthias Sohn /** {@inheritDoc} */ 4330628e3bSMatthias Sohn @Override createArchiveOutputStream(OutputStream s)44c0c4c6f0SDavid Ostrovsky public ArchiveOutputStream createArchiveOutputStream(OutputStream s) 45c0c4c6f0SDavid Ostrovsky throws IOException { 46c0c4c6f0SDavid Ostrovsky return createArchiveOutputStream(s, 47c0c4c6f0SDavid Ostrovsky Collections.<String, Object> emptyMap()); 48c0c4c6f0SDavid Ostrovsky } 49c0c4c6f0SDavid Ostrovsky 5032022e97SMatthias Sohn /** {@inheritDoc} */ 5130628e3bSMatthias Sohn @Override createArchiveOutputStream(OutputStream s, Map<String, Object> o)52c0c4c6f0SDavid Ostrovsky public ArchiveOutputStream createArchiveOutputStream(OutputStream s, 53c0c4c6f0SDavid Ostrovsky Map<String, Object> o) throws IOException { 54f07b6023SDavid Pursehouse TarArchiveOutputStream out = new TarArchiveOutputStream(s, 55fbf6ce65SDavid Pursehouse UTF_8.name()); 56ebfe85d0SJonathan Nieder out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); 57ebfe85d0SJonathan Nieder out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); 58c0c4c6f0SDavid Ostrovsky return applyFormatOptions(out, o); 5956276d05SJonathan Nieder } 6056276d05SJonathan Nieder 6132022e97SMatthias Sohn /** {@inheritDoc} */ 621448ec37SNaoki Takezoe @Override putEntry(ArchiveOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader)631448ec37SNaoki Takezoe public void putEntry(ArchiveOutputStream out, 641448ec37SNaoki Takezoe ObjectId tree, String path, FileMode mode, ObjectLoader loader) 651448ec37SNaoki Takezoe throws IOException { 6656276d05SJonathan Nieder if (mode == FileMode.SYMLINK) { 6756276d05SJonathan Nieder final TarArchiveEntry entry = new TarArchiveEntry( 6856276d05SJonathan Nieder path, TarConstants.LF_SYMLINK); 69fbf6ce65SDavid Pursehouse entry.setLinkName(new String(loader.getCachedBytes(100), UTF_8)); 7056276d05SJonathan Nieder out.putArchiveEntry(entry); 7156276d05SJonathan Nieder out.closeArchiveEntry(); 7256276d05SJonathan Nieder return; 7356276d05SJonathan Nieder } 7456276d05SJonathan Nieder 752ecc27dbSJonathan Nieder // TarArchiveEntry detects directories by checking 762ecc27dbSJonathan Nieder // for '/' at the end of the filename. 770bc98f17SRobin Rosenberg if (path.endsWith("/") && mode != FileMode.TREE) //$NON-NLS-1$ 782ecc27dbSJonathan Nieder throw new IllegalArgumentException(MessageFormat.format( 792ecc27dbSJonathan Nieder ArchiveText.get().pathDoesNotMatchMode, path, mode)); 800bc98f17SRobin Rosenberg if (!path.endsWith("/") && mode == FileMode.TREE) //$NON-NLS-1$ 810bc98f17SRobin Rosenberg path = path + "/"; //$NON-NLS-1$ 822ecc27dbSJonathan Nieder 8356276d05SJonathan Nieder final TarArchiveEntry entry = new TarArchiveEntry(path); 84b2610246SYasuhiro Takagi 85b2610246SYasuhiro Takagi if (tree instanceof RevCommit) { 86b2610246SYasuhiro Takagi long t = ((RevCommit) tree).getCommitTime() * 1000L; 87b2610246SYasuhiro Takagi entry.setModTime(t); 88b2610246SYasuhiro Takagi } 89b2610246SYasuhiro Takagi 902ecc27dbSJonathan Nieder if (mode == FileMode.TREE) { 912ecc27dbSJonathan Nieder out.putArchiveEntry(entry); 922ecc27dbSJonathan Nieder out.closeArchiveEntry(); 932ecc27dbSJonathan Nieder return; 942ecc27dbSJonathan Nieder } 952ecc27dbSJonathan Nieder 962ecc27dbSJonathan Nieder if (mode == FileMode.REGULAR_FILE) { 972ecc27dbSJonathan Nieder // ok 982ecc27dbSJonathan Nieder } else if (mode == FileMode.EXECUTABLE_FILE) { 9956276d05SJonathan Nieder entry.setMode(mode.getBits()); 10056276d05SJonathan Nieder } else { 1012ecc27dbSJonathan Nieder // Unsupported mode (e.g., GITLINK). 1022ecc27dbSJonathan Nieder throw new IllegalArgumentException(MessageFormat.format( 1032ecc27dbSJonathan Nieder ArchiveText.get().unsupportedMode, mode)); 10456276d05SJonathan Nieder } 10556276d05SJonathan Nieder entry.setSize(loader.getSize()); 10656276d05SJonathan Nieder out.putArchiveEntry(entry); 10756276d05SJonathan Nieder loader.copyTo(out); 10856276d05SJonathan Nieder out.closeArchiveEntry(); 10956276d05SJonathan Nieder } 11056cb2d92SJonathan Nieder 11132022e97SMatthias Sohn /** {@inheritDoc} */ 11230628e3bSMatthias Sohn @Override suffixes()11356cb2d92SJonathan Nieder public Iterable<String> suffixes() { 11456cb2d92SJonathan Nieder return SUFFIXES; 11556cb2d92SJonathan Nieder } 1160a14909bSJonathan Nieder 11732022e97SMatthias Sohn /** {@inheritDoc} */ 1180a14909bSJonathan Nieder @Override equals(Object other)1190a14909bSJonathan Nieder public boolean equals(Object other) { 1200a14909bSJonathan Nieder return (other instanceof TarFormat); 1210a14909bSJonathan Nieder } 1220a14909bSJonathan Nieder 12332022e97SMatthias Sohn /** {@inheritDoc} */ 1240a14909bSJonathan Nieder @Override hashCode()1250a14909bSJonathan Nieder public int hashCode() { 1260a14909bSJonathan Nieder return getClass().hashCode(); 1270a14909bSJonathan Nieder } 12856276d05SJonathan Nieder } 129