diff options
17 files changed, 295 insertions, 73 deletions
diff --git a/.eclipse_iplog b/.eclipse_iplog index 8707214f73..2f518e1235 100644 --- a/.eclipse_iplog +++ b/.eclipse_iplog @@ -120,9 +120,3 @@ license = Apache License, 2.0 use = unmodified source & binary state = approved - -[CQ "5135"] - description = Protocol Buffers Version: 2.4.0a (ATO CQ4876) - license = New BSD license - use = unmodified source & binary - state = approved diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java index 30d437540f..8ed51fb908 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java @@ -303,6 +303,17 @@ public abstract class LocalDiskRepositoryTestCase { } /** + * Adds a repository to the list of repositories which is closed at the end + * of the tests + * + * @param r + * the repository to be closed + */ + public void addRepoToClose(Repository r) { + toClose.add(r); + } + + /** * Creates a new unique directory for a test repository * * @param bare diff --git a/org.eclipse.jgit.pgm/jgit.sh b/org.eclipse.jgit.pgm/jgit.sh index e6a2119ef1..9ff59d6122 100644 --- a/org.eclipse.jgit.pgm/jgit.sh +++ b/org.eclipse.jgit.pgm/jgit.sh @@ -41,6 +41,20 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cmd= +for a in "$@" +do + case "$a" in + -*) continue ;; + *) cmd=$a; break; ;; + esac +done + +use_pager= +case "$cmd" in +diff) use_pager=1 ;; +log) use_pager=1 ;; +esac this_script=`which "$0" 2>/dev/null` [ $? -gt 0 -a -f "$0" ] && this_script="$0" @@ -58,7 +72,7 @@ CYGWIN*) cp=`cygpath --windows --mixed --path "$cp"` ;; Darwin) - if test -e /System/Library/Frameworks/JavaVM.framework + if [ -e /System/Library/Frameworks/JavaVM.framework ] then java_args=' -Dcom.apple.mrj.application.apple.menu.about.name=JGit @@ -74,10 +88,25 @@ CLASSPATH="$cp" export CLASSPATH java=java -if test -n "$JAVA_HOME" +if [ -n "$JAVA_HOME" ] then java="$JAVA_HOME/bin/java" fi -exec "$java" $java_args org.eclipse.jgit.pgm.Main "$@" -exit 1 +if [ -n "$use_pager" ] +then + use_pager=${GIT_PAGER:-${PAGER:-less}} + [ cat = "$use_pager" ] && use_pager= +fi + +if [ -n "$use_pager" ] +then + LESS=${LESS:-FSRX} + export LESS + + "$java" $java_args org.eclipse.jgit.pgm.Main "$@" | $use_pager + exit +else + exec "$java" $java_args org.eclipse.jgit.pgm.Main "$@" + exit 1 +fi diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/RepositoryIndexTable.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/RepositoryIndexTable.java index 794db6e5e2..36afd13229 100644 --- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/RepositoryIndexTable.java +++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/RepositoryIndexTable.java @@ -87,4 +87,21 @@ public interface RepositoryIndexTable { */ public void putUnique(RepositoryName name, RepositoryKey key) throws DhtException, TimeoutException; + + /** + * Remove the association of a name to an identifier. + * <p> + * This method must use some sort of transaction system to ensure the name + * is removed only if it currently references {@code key}. This may require + * running some sort of lock management service in parallel to the database. + * + * @param name + * name of the repository. + * @param key + * internal key defining the repository. + * @throws DhtException + * @throws TimeoutException + */ + public void remove(RepositoryName name, RepositoryKey key) + throws DhtException, TimeoutException; } diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/cache/CacheRepositoryIndexTable.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/cache/CacheRepositoryIndexTable.java index 5ff43910f3..b50092c6d1 100644 --- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/cache/CacheRepositoryIndexTable.java +++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/cache/CacheRepositoryIndexTable.java @@ -128,4 +128,18 @@ public class CacheRepositoryIndexTable implements RepositoryIndexTable { throw new TimeoutException(); } } + + public void remove(RepositoryName name, RepositoryKey key) + throws DhtException, TimeoutException { + db.remove(name, key); + + Sync<Void> sync = Sync.create(); + CacheKey memKey = ns.key(name); + client.modify(singleton(Change.remove(memKey)), sync); + try { + sync.get(options.getTimeout()); + } catch (InterruptedException e) { + throw new TimeoutException(); + } + } } diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/memory/MemRepositoryIndexTable.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/memory/MemRepositoryIndexTable.java index 46a1fd619a..000ff77327 100644 --- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/memory/MemRepositoryIndexTable.java +++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/memory/MemRepositoryIndexTable.java @@ -78,4 +78,16 @@ final class MemRepositoryIndexTable implements RepositoryIndexTable { throw new DhtException(MessageFormat.format( DhtText.get().repositoryAlreadyExists, name.asString())); } + + public void remove(RepositoryName name, RepositoryKey key) + throws DhtException, TimeoutException { + boolean ok = table.compareAndSet( + name.asBytes(), + colId.name(), + key.asBytes(), + null); + if (!ok) + throw new DhtException(MessageFormat.format( + DhtText.get().repositoryAlreadyExists, name.asString())); + } } diff --git a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/util/AbstractWriteBuffer.java b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/util/AbstractWriteBuffer.java index d40cbe31ad..ad55206fe7 100644 --- a/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/util/AbstractWriteBuffer.java +++ b/org.eclipse.jgit.storage.dht/src/org/eclipse/jgit/storage/dht/spi/util/AbstractWriteBuffer.java @@ -82,6 +82,8 @@ public abstract class AbstractWriteBuffer implements WriteBuffer { private final List<Future<?>> running; + private final Object runningLock; + private final Semaphore spaceAvailable; private int queuedCount; @@ -102,6 +104,7 @@ public abstract class AbstractWriteBuffer implements WriteBuffer { this.executor = executor; this.bufferSize = bufferSize; this.running = new LinkedList<Future<?>>(); + this.runningLock = new Object(); this.spaceAvailable = new Semaphore(bufferSize); } @@ -189,14 +192,18 @@ public abstract class AbstractWriteBuffer implements WriteBuffer { } } - checkRunningTasks(true); + synchronized (runningLock) { + checkRunningTasks(true); + } } finally { flushing = false; } } public void abort() throws DhtException { - checkRunningTasks(true); + synchronized (runningLock) { + checkRunningTasks(true); + } } private void acquireSpace(int sz) throws DhtException { @@ -259,9 +266,11 @@ public abstract class AbstractWriteBuffer implements WriteBuffer { return; } - if (!flushing) - checkRunningTasks(false); - running.add(executor.submit(op)); + synchronized (runningLock) { + if (!flushing) + checkRunningTasks(false); + running.add(executor.submit(op)); + } } /** @@ -284,8 +293,10 @@ public abstract class AbstractWriteBuffer implements WriteBuffer { int size) throws DhtException { int permits = permitsForSize(size); WrappedCallback<T> op = new WrappedCallback<T>(callback, permits); - checkRunningTasks(false); - running.add(op); + synchronized (runningLock) { + checkRunningTasks(false); + running.add(op); + } return op; } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java index f21dc4a0be..2e3345756d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java @@ -49,12 +49,14 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.util.Collections; +import java.util.List; import org.eclipse.jgit.api.ListBranchCommand.ListMode; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryTestCase; @@ -99,6 +101,7 @@ public class CloneCommandTest extends RepositoryTestCase { command.setURI("file://" + git.getRepository().getWorkTree().getPath()); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); assertNotNull(git2); ObjectId id = git2.getRepository().resolve("tag-for-blob"); assertNotNull(id); @@ -133,9 +136,51 @@ public class CloneCommandTest extends RepositoryTestCase { command.setURI("file://" + git.getRepository().getWorkTree().getPath()); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); + + assertNotNull(git2); + assertEquals(git2.getRepository().getFullBranch(), + "refs/heads/master"); + assertEquals( + "refs/heads/master, refs/remotes/origin/master, refs/remotes/origin/test", + allRefNames(git2.branchList().setListMode(ListMode.ALL) + .call())); + + // Same thing, but now without checkout + directory = createTempDirectory("testCloneRepositoryWithBranch_bare"); + command = Git.cloneRepository(); + command.setBranch("refs/heads/master"); + command.setDirectory(directory); + command.setURI("file://" + + git.getRepository().getWorkTree().getPath()); + command.setNoCheckout(true); + git2 = command.call(); + addRepoToClose(git2.getRepository()); + assertNotNull(git2); assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master"); + assertEquals( + "refs/remotes/origin/master, refs/remotes/origin/test", + allRefNames(git2.branchList().setListMode(ListMode.ALL) + .call())); + + // Same thing, but now test with bare repo + directory = createTempDirectory("testCloneRepositoryWithBranch_bare"); + command = Git.cloneRepository(); + command.setBranch("refs/heads/master"); + command.setDirectory(directory); + command.setURI("file://" + + git.getRepository().getWorkTree().getPath()); + command.setBare(true); + git2 = command.call(); + addRepoToClose(git2.getRepository()); + + assertNotNull(git2); + assertEquals(git2.getRepository().getFullBranch(), + "refs/heads/master"); + assertEquals("refs/heads/master, refs/heads/test", allRefNames(git2 + .branchList().setListMode(ListMode.ALL).call())); } catch (Exception e) { fail(e.getMessage()); } @@ -153,16 +198,46 @@ public class CloneCommandTest extends RepositoryTestCase { command.setURI("file://" + git.getRepository().getWorkTree().getPath()); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); assertNotNull(git2); assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master"); - assertEquals(1, git2.branchList().setListMode(ListMode.REMOTE) - .call().size()); + assertEquals("refs/remotes/origin/master", + allRefNames(git2.branchList() + .setListMode(ListMode.REMOTE).call())); + + // Same thing, but now test with bare repo + directory = createTempDirectory("testCloneRepositoryWithBranch_bare"); + command = Git.cloneRepository(); + command.setBranch("refs/heads/master"); + command.setBranchesToClone(Collections + .singletonList("refs/heads/master")); + command.setDirectory(directory); + command.setURI("file://" + + git.getRepository().getWorkTree().getPath()); + command.setBare(true); + git2 = command.call(); + addRepoToClose(git2.getRepository()); + assertNotNull(git2); + assertEquals(git2.getRepository().getFullBranch(), + "refs/heads/master"); + assertEquals("refs/heads/master", allRefNames(git2 + .branchList().setListMode(ListMode.ALL).call())); } catch (Exception e) { fail(e.getMessage()); } } + public static String allRefNames(List<Ref> refs) { + StringBuilder sb = new StringBuilder(); + for (Ref f : refs) { + if (sb.length() > 0) + sb.append(", "); + sb.append(f.getName()); + } + return sb.toString(); + } + public static File createTempDirectory(String name) throws IOException { final File temp; temp = File.createTempFile(name, Long.toString(System.nanoTime())); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java index 8aacbb063c..8031769204 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java @@ -72,6 +72,7 @@ public class GitConstructionTest extends RepositoryTestCase { .setURI(db.getDirectory().toURI().toString()) .setDirectory(createUniqueTestGitDir(true)).call() .getRepository(); + addRepoToClose(bareRepo); } @Override @@ -88,7 +89,7 @@ public class GitConstructionTest extends RepositoryTestCase { assertEquals(1, git.branchList().call().size()); git = Git.wrap(bareRepo); - assertEquals(2, git.branchList().setListMode(ListMode.ALL).call() + assertEquals(1, git.branchList().setListMode(ListMode.ALL).call() .size()); try { @@ -105,7 +106,7 @@ public class GitConstructionTest extends RepositoryTestCase { assertEquals(1, git.branchList().call().size()); git = Git.open(bareRepo.getDirectory()); - assertEquals(2, git.branchList().setListMode(ListMode.ALL).call() + assertEquals(1, git.branchList().setListMode(ListMode.ALL).call() .size()); git = Git.open(db.getWorkTree()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java index 15aafc9060..28c54c269f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java @@ -70,6 +70,7 @@ public class InitCommandTest extends RepositoryTestCase { InitCommand command = new InitCommand(); command.setDirectory(directory); Repository repository = command.call().getRepository(); + addRepoToClose(repository); assertNotNull(repository); } catch (Exception e) { fail(e.getMessage()); @@ -84,6 +85,7 @@ public class InitCommandTest extends RepositoryTestCase { command.setDirectory(directory); command.setBare(true); Repository repository = command.call().getRepository(); + addRepoToClose(repository); assertNotNull(repository); assertTrue(repository.isBare()); } catch (Exception e) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java index 81aa25f981..a4a5a2671b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java @@ -89,6 +89,8 @@ public class LsRemoteCommandTest extends RepositoryTestCase { + git.getRepository().getWorkTree().getPath()); command.setCloneAllBranches(true); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); + LsRemoteCommand lsRemoteCommand = git2.lsRemote(); Collection<Ref> refs = lsRemoteCommand.call(); @@ -109,6 +111,7 @@ public class LsRemoteCommandTest extends RepositoryTestCase { + git.getRepository().getWorkTree().getPath()); command.setCloneAllBranches(true); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); LsRemoteCommand lsRemoteCommand = git2.lsRemote(); lsRemoteCommand.setTags(true); @@ -130,6 +133,7 @@ public class LsRemoteCommandTest extends RepositoryTestCase { + git.getRepository().getWorkTree().getPath()); command.setCloneAllBranches(true); Git git2 = command.call(); + addRepoToClose(git2.getRepository()); LsRemoteCommand lsRemoteCommand = git2.lsRemote(); lsRemoteCommand.setHeads(true); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java index df89674e6f..719cc66671 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java @@ -46,7 +46,9 @@ package org.eclipse.jgit.transport; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.File; @@ -54,8 +56,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; +import java.text.MessageFormat; import java.util.zip.Deflater; +import org.eclipse.jgit.JGitText; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; @@ -69,6 +73,7 @@ import org.eclipse.jgit.storage.file.ObjectDirectoryPackParser; import org.eclipse.jgit.storage.file.PackFile; import org.eclipse.jgit.util.NB; import org.eclipse.jgit.util.TemporaryBuffer; +import org.eclipse.jgit.util.io.UnionInputStream; import org.junit.After; import org.junit.Test; @@ -177,6 +182,33 @@ public class PackParserTest extends RepositoryTestCase { p.parse(NullProgressMonitor.INSTANCE); } + @Test + public void testPackWithTrailingGarbage() throws Exception { + TestRepository d = new TestRepository(db); + RevBlob a = d.blob("a"); + + TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024); + packHeader(pack, 1); + pack.write((Constants.OBJ_REF_DELTA) << 4 | 4); + a.copyRawTo(pack); + deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' }); + digest(pack); + + PackParser p = index(new UnionInputStream( + new ByteArrayInputStream(pack.toByteArray()), + new ByteArrayInputStream(new byte[] { 0x7e }))); + p.setAllowThin(true); + p.setCheckEofAfterPackFooter(true); + try { + p.parse(NullProgressMonitor.INSTANCE); + fail("Pack with trailing garbage was accepted"); + } catch (IOException err) { + assertEquals( + MessageFormat.format(JGitText.get().expectedEOFReceived, "\\x73"), + err.getMessage()); + } + } + private void packHeader(TemporaryBuffer.Heap tinyPack, int cnt) throws IOException { final byte[] hdr = new byte[8]; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java index 39652306b0..aa8cf6e30a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -98,6 +98,8 @@ public class CloneCommand implements Callable<Git> { private boolean cloneAllBranches; + private boolean noCheckout; + private Collection<String> branchesToClone; /** @@ -112,7 +114,8 @@ public class CloneCommand implements Callable<Git> { URIish u = new URIish(uri); Repository repository = init(u); FetchResult result = fetch(repository, u); - checkout(repository, result); + if (!noCheckout) + checkout(repository, result); return new Git(repository); } catch (IOException ioe) { throw new JGitInternalException(ioe.getMessage(), ioe); @@ -140,7 +143,8 @@ public class CloneCommand implements Callable<Git> { RemoteConfig config = new RemoteConfig(repo.getConfig(), remote); config.addURI(u); - final String dst = Constants.R_REMOTES + config.getName(); + final String dst = bare ? Constants.R_HEADS : Constants.R_REMOTES + + config.getName(); RefSpec refSpec = new RefSpec(); refSpec = refSpec.setForceUpdate(true); refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", dst + "/*"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -368,4 +372,16 @@ public class CloneCommand implements Callable<Git> { return this; } + /** + * @param noCheckout + * if set to <code>true</code> no branch will be checked out + * after the clone. This enhances performance of the clone + * command when there is no need for a checked out branch. + * @return {@code this} + */ + public CloneCommand setNoCheckout(boolean noCheckout) { + this.noCheckout = noCheckout; + return this; + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/EditList.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/EditList.java index 445ff7afe2..be753bee8c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/EditList.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/EditList.java @@ -43,12 +43,12 @@ package org.eclipse.jgit.diff; -import java.util.AbstractList; import java.util.ArrayList; -import java.util.Collection; /** Specialized list of {@link Edit}s in a document. */ -public class EditList extends AbstractList<Edit> { +public class EditList extends ArrayList<Edit> { + private static final long serialVersionUID = 1L; + /** * Construct an edit list containing a single edit. * @@ -62,11 +62,9 @@ public class EditList extends AbstractList<Edit> { return res; } - private final ArrayList<Edit> container; - /** Create a new, empty edit list. */ public EditList() { - container = new ArrayList<Edit>(); + super(16); } /** @@ -77,53 +75,11 @@ public class EditList extends AbstractList<Edit> { * added to the list, it will be grown to support them. */ public EditList(int capacity) { - container = new ArrayList<Edit>(capacity); - } - - @Override - public int size() { - return container.size(); - } - - @Override - public Edit get(final int index) { - return container.get(index); - } - - @Override - public Edit set(final int index, final Edit element) { - return container.set(index, element); - } - - @Override - public void add(final int index, final Edit element) { - container.add(index, element); - } - - @Override - public boolean addAll(Collection<? extends Edit> c) { - return container.addAll(c); - } - - @Override - public Edit remove(final int index) { - return container.remove(index); - } - - @Override - public int hashCode() { - return container.hashCode(); - } - - @Override - public boolean equals(final Object o) { - if (o instanceof EditList) - return container.equals(((EditList) o).container); - return false; + super(capacity); } @Override public String toString() { - return "EditList" + container.toString(); + return "EditList" + super.toString(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java index 6f0c6c3b36..4bbe3a0048 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java @@ -136,6 +136,8 @@ public abstract class PackParser { private boolean needBaseObjectIds; + private boolean checkEofAfterPackFooter; + private long objectCount; private PackedObjectInfo[] entries; @@ -282,6 +284,21 @@ public abstract class PackParser { this.needBaseObjectIds = b; } + /** @return true if the EOF should be read from the input after the footer. */ + public boolean isCheckEofAfterPackFooter() { + return checkEofAfterPackFooter; + } + + /** + * Ensure EOF is read from the input stream after the footer. + * + * @param b + * true if the EOF should be read; false if it is not checked. + */ + public void setCheckEofAfterPackFooter(boolean b) { + checkEofAfterPackFooter = b; + } + /** @return the new objects that were sent by the user */ public ObjectIdSubclassMap<ObjectId> getNewObjectIds() { if (newObjectIds != null) @@ -782,6 +799,25 @@ public abstract class PackParser { System.arraycopy(buf, c, srcHash, 0, 20); use(20); + // The input stream should be at EOF at this point. We do not support + // yielding back any remaining buffered data after the pack footer, so + // protocols that embed a pack stream are required to either end their + // stream with the pack, or embed the pack with a framing system like + // the SideBandInputStream does. + + if (bAvail != 0) + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().expectedEOFReceived, + "\\x" + Integer.toHexString(buf[bOffset] & 0xff))); + + if (isCheckEofAfterPackFooter()) { + int eof = in.read(); + if (0 <= eof) + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().expectedEOFReceived, + "\\x" + Integer.toHexString(eof))); + } + if (!Arrays.equals(actHash, srcHash)) throw new CorruptObjectException( JGitText.get().corruptObjectPackfileChecksumIncorrect); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index e2ab5f6780..f1f9b0f44d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -825,6 +825,7 @@ public class ReceivePack { parser.setAllowThin(true); parser.setNeedNewObjectIds(checkReferencedIsReachable); parser.setNeedBaseObjectIds(checkReferencedIsReachable); + parser.setCheckEofAfterPackFooter(!biDirectionalPipe); parser.setObjectChecking(isCheckReceivedObjects()); parser.setLockMessage(lockMsg); packLock = parser.parse(receiving, resolving); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 50f57130c3..0e50b937b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -55,6 +55,7 @@ import java.util.Map; import java.util.Set; import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.lib.Constants; @@ -757,6 +758,16 @@ public class UploadPack { final boolean sideband = options.contains(OPTION_SIDE_BAND) || options.contains(OPTION_SIDE_BAND_64K); + if (!biDirectionalPipe) { + // Ensure the request was fully consumed. Any remaining input must + // be a protocol error. If we aren't at EOF the implementation is broken. + int eof = rawIn.read(); + if (0 <= eof) + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().expectedEOFReceived, + "\\x" + Integer.toHexString(eof))); + } + ProgressMonitor pm = NullProgressMonitor.INSTANCE; OutputStream packOut = rawOut; SideBandOutputStream msgOut = null; |