xref: /JGit/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
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