* [JGIT PATCH 0/5] Support receive.fsckobjects
@ 2008-10-01 1:31 Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 1/5] Expose RawParseUtils.match to application callers Shawn O. Pearce
0 siblings, 1 reply; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
This series adds support for receive.fsckobjects, but on the fetch
side of the connection. Perhaps it should be transfer.fsckobjects
or fetch.fsckobjects, but git.git doesn't support either of those
right now.
I mainly need this series because I'm fetching out of untrusted
bundles. The content of the bundle has to pass git-fsck for it
to be considered safe.
The ObjectChecker class covers the same rules as git-fsck does, and
is perhaps even stricter on some of the things git-fsck lets slide.
I think git-fsck is too lenient in some areas, and I'd like to try
and improve the rules more in git.git, but I don't have time for
it right now.
Shawn O. Pearce (5):
Expose RawParseUtils.match to application callers
Fix UnpackedObjectLoader.getBytes to return a copy
Object validation tests for "jgit fsck"
Expose the critical receive configuration options to JGit
Honor receive.fsckobjects during any fetch connection
.../org/spearce/jgit/lib/ObjectCheckerTest.java | 1294 ++++++++++++++++++++
.../src/org/spearce/jgit/lib/ObjectChecker.java | 352 ++++++
.../src/org/spearce/jgit/lib/ObjectLoader.java | 7 +-
.../org/spearce/jgit/lib/PackedObjectLoader.java | 7 -
.../src/org/spearce/jgit/lib/RepositoryConfig.java | 10 +
.../src/org/spearce/jgit/lib/TransferConfig.java | 56 +
.../org/spearce/jgit/lib/UnpackedObjectLoader.java | 4 -
.../jgit/transport/BasePackFetchConnection.java | 1 +
.../src/org/spearce/jgit/transport/IndexPack.java | 60 +-
.../src/org/spearce/jgit/transport/Transport.java | 24 +
.../spearce/jgit/transport/TransportBundle.java | 10 +-
.../jgit/transport/WalkFetchConnection.java | 26 +-
.../src/org/spearce/jgit/util/RawParseUtils.java | 23 +-
13 files changed, 1842 insertions(+), 32 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
^ permalink raw reply [flat|nested] 11+ messages in thread
* [JGIT PATCH 1/5] Expose RawParseUtils.match to application callers
2008-10-01 1:31 [JGIT PATCH 0/5] Support receive.fsckobjects Shawn O. Pearce
@ 2008-10-01 1:31 ` Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Shawn O. Pearce
0 siblings, 1 reply; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
This utility can be useful when parsing a buffer directly, such as
for a commit or tag object's header lines.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/util/RawParseUtils.java | 13 ++++++++++++-
1 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
index dbc2e83..2ab3bfe 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
@@ -61,7 +61,18 @@
digits[i] = (byte) (i - '0');
}
- private static final int match(final byte[] b, int ptr, final byte[] src) {
+ /**
+ * Determine if b[ptr] matches src.
+ *
+ * @param b
+ * the buffer to scan.
+ * @param ptr
+ * first position within b, this should match src[0].
+ * @param src
+ * the buffer to test for equality with b.
+ * @return ptr += src.length if b[ptr..src.length] == src; else -1.
+ */
+ public static final int match(final byte[] b, int ptr, final byte[] src) {
if (ptr + src.length >= b.length)
return -1;
for (int i = 0; i < src.length; i++, ptr++)
--
1.6.0.2.569.g798a2a
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy
2008-10-01 1:31 ` [JGIT PATCH 1/5] Expose RawParseUtils.match to application callers Shawn O. Pearce
@ 2008-10-01 1:31 ` Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 3/5] Object validation tests for "jgit fsck" Shawn O. Pearce
2008-10-09 21:46 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Jonas Fonseca
0 siblings, 2 replies; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
The contract for ObjectLoader.getBytes() says the caller can modify
the returned array. UnpackedObjectLoader must copy the data and not
return its internal cached byte array.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/ObjectLoader.java | 7 ++++++-
.../org/spearce/jgit/lib/PackedObjectLoader.java | 7 -------
.../org/spearce/jgit/lib/UnpackedObjectLoader.java | 4 ----
3 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
index 5282491..87e861f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
@@ -105,7 +105,12 @@ protected void setId(final ObjectId id) {
* @throws IOException
* the object cannot be read.
*/
- public abstract byte[] getBytes() throws IOException;
+ public final byte[] getBytes() throws IOException {
+ final byte[] data = getCachedBytes();
+ final byte[] copy = new byte[data.length];
+ System.arraycopy(data, 0, copy, 0, data.length);
+ return data;
+ }
/**
* Obtain a reference to the (possibly cached) bytes of this object.
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
index fa414d6..35983fe 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
@@ -80,13 +80,6 @@ public long getDataOffset() {
return dataOffset;
}
- public final byte[] getBytes() throws IOException {
- final byte[] data = getCachedBytes();
- final byte[] copy = new byte[data.length];
- System.arraycopy(data, 0, copy, 0, data.length);
- return data;
- }
-
/**
* Copy raw object representation from storage to provided output stream.
* <p>
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
index 3c61254..3ad273f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
@@ -200,10 +200,6 @@ public long getSize() {
return objectSize;
}
- public byte[] getBytes() {
- return bytes;
- }
-
@Override
public byte[] getCachedBytes() throws IOException {
return bytes;
--
1.6.0.2.569.g798a2a
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [JGIT PATCH 3/5] Object validation tests for "jgit fsck"
2008-10-01 1:31 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Shawn O. Pearce
@ 2008-10-01 1:31 ` Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Shawn O. Pearce
2008-10-09 21:46 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Jonas Fonseca
1 sibling, 1 reply; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
The ObjectChecker provides strict validation rules for objects. If
any commit, tag or tree object is malformed in a way that might cause
a Git implementation to misinterpret the data CorruptObjectException
is thrown back to the caller for error handling.
Due to the shear size of the validation code this change provides
only the validation code and its unit tests. It is at least as
paranoid as git.git's "git-fsck" is on the same object types, but
is actually a bit stricter about the commit and tag objects having
the well known header fields populated correctly.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/lib/ObjectCheckerTest.java | 1294 ++++++++++++++++++++
.../src/org/spearce/jgit/lib/ObjectChecker.java | 352 ++++++
.../src/org/spearce/jgit/util/RawParseUtils.java | 10 +-
3 files changed, 1650 insertions(+), 6 deletions(-)
create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
new file mode 100644
index 0000000..fa37fb5
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import junit.framework.TestCase;
+
+import org.spearce.jgit.errors.CorruptObjectException;
+
+public class ObjectCheckerTest extends TestCase {
+ private ObjectChecker checker;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ checker = new ObjectChecker();
+ }
+
+ public void testInvalidType() {
+ try {
+ checker.check(Constants.OBJ_BAD, new byte[0]);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException e) {
+ final String m = e.getMessage();
+ assertEquals("Invalid object type: " + Constants.OBJ_BAD, m);
+ }
+ }
+
+ public void testCheckBlob() throws CorruptObjectException {
+ // Any blob should pass...
+ checker.checkBlob(new byte[0]);
+ checker.checkBlob(new byte[1]);
+
+ checker.check(Constants.OBJ_BLOB, new byte[0]);
+ checker.check(Constants.OBJ_BLOB, new byte[1]);
+ }
+
+ public void testValidCommitNoParent() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+ b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testValidCommitBlankAuthor() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author <> 0 +0000\n");
+ b.append("committer <> 0 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testValidCommit1Parent() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+ b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testValidCommit2Parent() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+ b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testValidCommit128Parent() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ for (int i = 0; i < 128; i++) {
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+ }
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+ b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testValidCommitNormalTime() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ final String when = "1222757360 -0730";
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> " + when + "\n");
+ b.append("committer A. U. Thor <author@localhost> " + when + "\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(Constants.OBJ_COMMIT, data);
+ }
+
+ public void testInvalidCommitNoTree1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tree header", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoTree2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("trie ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tree header", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoTree3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tree header", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoTree4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree\t");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tree header", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidTree1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tree", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidTree2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append("z\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tree", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidTree3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9b");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tree", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidTree4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tree", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidParent1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid parent", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidParent2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid parent", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidParent3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid parent", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidParent4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append("z\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid parent", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidParent5() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("parent\t");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("no author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoAuthor() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("no author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoCommitter1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("no committer", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitNoCommitter2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+ b.append("\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("no committer", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor <foo 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author A. U. Thor foo> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author a <b> +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor5() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author a <b>\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor6() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author a <b> z");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidAuthor7() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author a <b> 1 z");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid author", e.getMessage());
+ }
+ }
+
+ public void testInvalidCommitInvalidCommitter() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("tree ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("author a <b> 1 +0000\n");
+ b.append("committer a <");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkCommit(data);
+ fail("Did not catch corrupt object");
+ } catch (CorruptObjectException e) {
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertEquals("invalid committer", e.getMessage());
+ }
+ }
+
+ public void testValidTag() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag test-tag\n");
+ b.append("tagger A. U. Thor <author@localhost> 1 +0000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTag(data);
+ checker.check(Constants.OBJ_TAG, data);
+ }
+
+ public void testInvalidTagNoObject1() {
+ final StringBuilder b = new StringBuilder();
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no object header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoObject2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object\t");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no object header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoObject3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("obejct ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no object header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoObject4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("zz9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid object", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoObject5() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append(" \n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid object", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoObject6() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid object", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoType1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no type header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoType2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type\tcommit\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no type header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoType3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("tpye commit\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no type header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoType4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tag header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoTagHeader1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tag header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoTagHeader2() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag\tfoo\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tag header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoTagHeader3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tga foo\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tag header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoTagHeader4() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag foo");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tagger header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagNoTaggerHeader1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag foo\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("no tagger header", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagInvalidTaggerHeader1() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag foo\n");
+ b.append("tagger \n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tagger", e.getMessage());
+ }
+ }
+
+ public void testInvalidTagInvalidTaggerHeader3() {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("object ");
+ b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+ b.append('\n');
+
+ b.append("type commit\n");
+ b.append("tag foo\n");
+ b.append("tagger a < 1 +000\n");
+
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTag(data);
+ fail("incorrectly accepted invalid tag");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid tagger", e.getMessage());
+ }
+ }
+
+ public void testValidEmptyTree() throws CorruptObjectException {
+ checker.checkTree(new byte[0]);
+ checker.check(Constants.OBJ_TREE, new byte[0]);
+ }
+
+ public void testValidTree1() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 regular-file");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTree2() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100755 executable");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTree3() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "40000 tree");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTree4() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "120000 symlink");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTree5() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "160000 git link");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting1() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 fooaaa");
+ entry(b, "100755 foobar");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting2() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100755 fooaaa");
+ entry(b, "100644 foobar");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting3() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "40000 a");
+ entry(b, "100644 b");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting4() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "40000 b");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting5() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a.c");
+ entry(b, "40000 a");
+ entry(b, "100644 a0c");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting6() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "40000 a");
+ entry(b, "100644 apple");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting7() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "40000 an orang");
+ entry(b, "40000 an orange");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testValidTreeSorting8() throws CorruptObjectException {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "100644 a0c");
+ entry(b, "100644 b");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(data);
+ }
+
+ public void testInvalidTreeModeStartsWithZero1() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "0 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("mode starts with '0'", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeStartsWithZero2() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "0100644 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("mode starts with '0'", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeStartsWithZero3() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "040000 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("mode starts with '0'", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeNotOctal1() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "8 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid mode character", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeNotOctal2() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "Z a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid mode character", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeNotSupportedMode1() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "1 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid mode 1", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeNotSupportedMode2() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "170000 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid mode " + 0170000, e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeModeMissingName() {
+ final StringBuilder b = new StringBuilder();
+ b.append("100644");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("truncated in mode", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeNameContainsSlash() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a/b");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("name contains '/'", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeNameIsEmpty() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 ");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("zero length name", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeTruncatedInName() {
+ final StringBuilder b = new StringBuilder();
+ b.append("100644 b");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("truncated in name", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeTruncatedInObjectId() {
+ final StringBuilder b = new StringBuilder();
+ b.append("100644 b\0\1\2");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("truncated in object id", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeBadSorting1() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 foobar");
+ entry(b, "100644 fooaaa");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("incorrectly sorted", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeBadSorting2() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "40000 a");
+ entry(b, "100644 a.c");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("incorrectly sorted", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeBadSorting3() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a0c");
+ entry(b, "40000 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("incorrectly sorted", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeDuplicateNames1() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "100644 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("duplicate entry names", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeDuplicateNames2() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "100755 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("duplicate entry names", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeDuplicateNames3() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "40000 a");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("duplicate entry names", e.getMessage());
+ }
+ }
+
+ public void testInvalidTreeDuplicateNames4() {
+ final StringBuilder b = new StringBuilder();
+ entry(b, "100644 a");
+ entry(b, "100644 a.c");
+ entry(b, "100644 a.d");
+ entry(b, "100644 a.e");
+ entry(b, "40000 a");
+ entry(b, "100644 zoo");
+ final byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.checkTree(data);
+ fail("incorrectly accepted an invalid tree");
+ } catch (CorruptObjectException e) {
+ assertEquals("duplicate entry names", e.getMessage());
+ }
+ }
+
+ private static void entry(final StringBuilder b, final String modeName) {
+ b.append(modeName);
+ b.append('\0');
+ for (int i = 0; i < Constants.OBJECT_ID_LENGTH; i++)
+ b.append((char) i);
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
new file mode 100644
index 0000000..0d165af
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import static org.spearce.jgit.util.RawParseUtils.match;
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+
+import org.spearce.jgit.errors.CorruptObjectException;
+import org.spearce.jgit.util.MutableInteger;
+
+/**
+ * Verifies that an object is formatted correctly.
+ * <p>
+ * Verifications made by this class only check that the fields of an object are
+ * formatted correctly. The ObjectId checksum of the object is not verified, and
+ * connectivity links between objects are also not verified. Its assumed that
+ * the caller can provide both of these validations on its own.
+ * <p>
+ * Instances of this class are not thread safe, but they may be reused to
+ * perform multiple object validations.
+ */
+public class ObjectChecker {
+ /** Header "tree " */
+ public static final byte[] tree = Constants.encodeASCII("tree ");
+
+ /** Header "parent " */
+ public static final byte[] parent = Constants.encodeASCII("parent ");
+
+ /** Header "author " */
+ public static final byte[] author = Constants.encodeASCII("author ");
+
+ /** Header "committer " */
+ public static final byte[] committer = Constants.encodeASCII("committer ");
+
+ /** Header "encoding " */
+ public static final byte[] encoding = Constants.encodeASCII("encoding ");
+
+ /** Header "object " */
+ public static final byte[] object = Constants.encodeASCII("object ");
+
+ /** Header "type " */
+ public static final byte[] type = Constants.encodeASCII("type ");
+
+ /** Header "tag " */
+ public static final byte[] tag = Constants.encodeASCII("tag ");
+
+ /** Header "tagger " */
+ public static final byte[] tagger = Constants.encodeASCII("tagger ");
+
+ private final MutableObjectId tempId = new MutableObjectId();
+
+ private final MutableInteger ptrout = new MutableInteger();
+
+ /**
+ * Check an object for parsing errors.
+ *
+ * @param objType
+ * type of the object. Must be a valid object type code in
+ * {@link Constants}.
+ * @param raw
+ * the raw data which comprises the object. This should be in the
+ * canonical format (that is the format used to generate the
+ * ObjectId of the object). The array is never modified.
+ * @throws CorruptObjectException
+ * if an error is identified.
+ */
+ public void check(final int objType, final byte[] raw)
+ throws CorruptObjectException {
+ switch (objType) {
+ case Constants.OBJ_COMMIT:
+ checkCommit(raw);
+ break;
+ case Constants.OBJ_TAG:
+ checkTag(raw);
+ break;
+ case Constants.OBJ_TREE:
+ checkTree(raw);
+ break;
+ case Constants.OBJ_BLOB:
+ checkBlob(raw);
+ break;
+ default:
+ throw new CorruptObjectException("Invalid object type: " + objType);
+ }
+ }
+
+ private int id(final byte[] raw, final int ptr) {
+ try {
+ tempId.fromString(raw, ptr);
+ return ptr + AnyObjectId.STR_LEN;
+ } catch (IllegalArgumentException e) {
+ return -1;
+ }
+ }
+
+ private int personIdent(final byte[] raw, int ptr) {
+ final int emailB = nextLF(raw, ptr, '<');
+ if (emailB == ptr || raw[emailB - 1] != '<')
+ return -1;
+
+ final int emailE = nextLF(raw, emailB, '>');
+ if (emailE == emailB || raw[emailE - 1] != '>')
+ return -1;
+ if (emailE == raw.length || raw[emailE] != ' ')
+ return -1;
+
+ parseBase10(raw, emailE + 1, ptrout); // when
+ ptr = ptrout.value;
+ if (emailE + 1 == ptr)
+ return -1;
+ if (ptr == raw.length || raw[ptr] != ' ')
+ return -1;
+
+ parseBase10(raw, ptr + 1, ptrout); // tz offset
+ if (ptr + 1 == ptrout.value)
+ return -1;
+ return ptrout.value;
+ }
+
+ /**
+ * Check a commit for errors.
+ *
+ * @param raw
+ * the commit data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ */
+ public void checkCommit(final byte[] raw) throws CorruptObjectException {
+ int ptr = 0;
+
+ if ((ptr = match(raw, ptr, tree)) < 0)
+ throw new CorruptObjectException("no tree header");
+ if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid tree");
+
+ while (match(raw, ptr, parent) >= 0) {
+ ptr += parent.length;
+ if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid parent");
+ }
+
+ if ((ptr = match(raw, ptr, author)) < 0)
+ throw new CorruptObjectException("no author");
+ if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid author");
+
+ if ((ptr = match(raw, ptr, committer)) < 0)
+ throw new CorruptObjectException("no committer");
+ if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid committer");
+ }
+
+ /**
+ * Check an annotated tag for errors.
+ *
+ * @param raw
+ * the tag data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ */
+ public void checkTag(final byte[] raw) throws CorruptObjectException {
+ int ptr = 0;
+
+ if ((ptr = match(raw, ptr, object)) < 0)
+ throw new CorruptObjectException("no object header");
+ if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid object");
+
+ if ((ptr = match(raw, ptr, type)) < 0)
+ throw new CorruptObjectException("no type header");
+ ptr = nextLF(raw, ptr, '\n');
+
+ if ((ptr = match(raw, ptr, tag)) < 0)
+ throw new CorruptObjectException("no tag header");
+ ptr = nextLF(raw, ptr, '\n');
+
+ if ((ptr = match(raw, ptr, tagger)) < 0)
+ throw new CorruptObjectException("no tagger header");
+ if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+ throw new CorruptObjectException("invalid tagger");
+ }
+
+ private static int lastPathChar(final int mode) {
+ return FileMode.TREE.equals(mode) ? '/' : '\0';
+ }
+
+ private static int pathCompare(final byte[] raw, int aPos, final int aEnd,
+ final int aMode, int bPos, final int bEnd, final int bMode) {
+ while (aPos < aEnd && bPos < bEnd) {
+ final int cmp = (raw[aPos++] & 0xff) - (raw[bPos++] & 0xff);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ if (aPos < aEnd)
+ return (raw[aPos] & 0xff) - lastPathChar(bMode);
+ if (bPos < bEnd)
+ return lastPathChar(aMode) - (raw[bPos] & 0xff);
+ return 0;
+ }
+
+ private static boolean duplicateName(final byte[] raw,
+ final int thisNamePos, final int thisNameEnd) {
+ final int sz = raw.length;
+ int nextPtr = thisNameEnd + 1 + Constants.OBJECT_ID_LENGTH;
+ for (;;) {
+ int nextMode = 0;
+ for (;;) {
+ if (nextPtr >= sz)
+ return false;
+ final byte c = raw[nextPtr++];
+ if (' ' == c)
+ break;
+ nextMode <<= 3;
+ nextMode += c - '0';
+ }
+
+ final int nextNamePos = nextPtr;
+ for (;;) {
+ if (nextPtr == sz)
+ return false;
+ final byte c = raw[nextPtr++];
+ if (c == 0)
+ break;
+ }
+ if (nextNamePos + 1 == nextPtr)
+ return false;
+
+ final int cmp = pathCompare(raw, thisNamePos, thisNameEnd,
+ FileMode.TREE.getBits(), nextNamePos, nextPtr - 1, nextMode);
+ if (cmp < 0)
+ return false;
+ else if (cmp == 0)
+ return true;
+
+ nextPtr += Constants.OBJECT_ID_LENGTH;
+ }
+ }
+
+ /**
+ * Check a canonical formatted tree for errors.
+ *
+ * @param raw
+ * the raw tree data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ */
+ public void checkTree(final byte[] raw) throws CorruptObjectException {
+ final int sz = raw.length;
+ int ptr = 0;
+ int lastNameB = 0, lastNameE = 0, lastMode = 0;
+
+ while (ptr < sz) {
+ int thisMode = 0;
+ for (;;) {
+ if (ptr == sz)
+ throw new CorruptObjectException("truncated in mode");
+ final byte c = raw[ptr++];
+ if (' ' == c)
+ break;
+ if (c < '0' || c > '7')
+ throw new CorruptObjectException("invalid mode character");
+ if (thisMode == 0 && c == '0')
+ throw new CorruptObjectException("mode starts with '0'");
+ thisMode <<= 3;
+ thisMode += c - '0';
+ }
+
+ if (FileMode.fromBits(thisMode).getObjectType() == Constants.OBJ_BAD)
+ throw new CorruptObjectException("invalid mode " + thisMode);
+
+ final int thisNameB = ptr;
+ for (;;) {
+ if (ptr == sz)
+ throw new CorruptObjectException("truncated in name");
+ final byte c = raw[ptr++];
+ if (c == 0)
+ break;
+ if (c == '/')
+ throw new CorruptObjectException("name contains '/'");
+ }
+ if (thisNameB + 1 == ptr)
+ throw new CorruptObjectException("zero length name");
+ if (duplicateName(raw, thisNameB, ptr - 1))
+ throw new CorruptObjectException("duplicate entry names");
+
+ if (lastNameB != 0) {
+ final int cmp = pathCompare(raw, lastNameB, lastNameE,
+ lastMode, thisNameB, ptr - 1, thisMode);
+ if (cmp > 0)
+ throw new CorruptObjectException("incorrectly sorted");
+ }
+
+ lastNameB = thisNameB;
+ lastNameE = ptr - 1;
+ lastMode = thisMode;
+
+ ptr += Constants.OBJECT_ID_LENGTH;
+ if (ptr > sz)
+ throw new CorruptObjectException("truncated in object id");
+ }
+ }
+
+ /**
+ * Check a blob for errors.
+ *
+ * @param raw
+ * the blob data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ */
+ public void checkBlob(final byte[] raw) throws CorruptObjectException {
+ // We can always assume the blob is valid.
+ }
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
index 2ab3bfe..c2c9b00 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
@@ -37,6 +37,10 @@
package org.spearce.jgit.util;
+import static org.spearce.jgit.lib.ObjectChecker.author;
+import static org.spearce.jgit.lib.ObjectChecker.committer;
+import static org.spearce.jgit.lib.ObjectChecker.encoding;
+
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
@@ -46,12 +50,6 @@
/** Handy utility functions to parse raw object contents. */
public final class RawParseUtils {
- private static final byte[] author = Constants.encodeASCII("author ");
-
- private static final byte[] committer = Constants.encodeASCII("committer ");
-
- private static final byte[] encoding = Constants.encodeASCII("encoding ");
-
private static final byte[] digits;
static {
--
1.6.0.2.569.g798a2a
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit
2008-10-01 1:31 ` [JGIT PATCH 3/5] Object validation tests for "jgit fsck" Shawn O. Pearce
@ 2008-10-01 1:31 ` Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 5/5] Honor receive.fsckobjects during any fetch connection Shawn O. Pearce
2008-10-01 20:54 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Robin Rosenberg
0 siblings, 2 replies; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
The TransferConfig object keeps track of basic transfer related options
from the transfer, receive and fetch groups within a git config file.
Right now we only care about receive.fsckobjects.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../src/org/spearce/jgit/lib/RepositoryConfig.java | 10 ++++
.../src/org/spearce/jgit/lib/TransferConfig.java | 56 ++++++++++++++++++++
2 files changed, 66 insertions(+), 0 deletions(-)
create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
index d3c29ac..d8fd2fa 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
@@ -92,6 +92,8 @@ public static RepositoryConfig openUserConfig() {
private CoreConfig core;
+ private TransferConfig transfer;
+
private List<Entry> entries;
private Map<String, Object> byName;
@@ -126,6 +128,13 @@ public CoreConfig getCore() {
}
/**
+ * @return transfer, fetch and receive configuration values
+ */
+ public TransferConfig getTransfer() {
+ return transfer;
+ }
+
+ /**
* Obtain an integer value from the configuration.
*
* @param section
@@ -667,6 +676,7 @@ public void load() throws IOException {
}
core = new CoreConfig(this);
+ transfer = new TransferConfig(this);
}
private void clear() {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
new file mode 100644
index 0000000..ff3a5eb
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+/**
+ * The standard "transfer", "fetch" and "receive" configuration parameters.
+ */
+public class TransferConfig {
+ private final boolean fsckObjects;
+
+ TransferConfig(final RepositoryConfig rc) {
+ fsckObjects = rc.getBoolean("receive", "fsckobjects", false);
+ }
+
+ /**
+ * @return strictly verify received objects?
+ */
+ public boolean isFsckObjects() {
+ return fsckObjects;
+ }
+}
--
1.6.0.2.569.g798a2a
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [JGIT PATCH 5/5] Honor receive.fsckobjects during any fetch connection
2008-10-01 1:31 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Shawn O. Pearce
@ 2008-10-01 1:31 ` Shawn O. Pearce
2008-10-01 20:54 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Robin Rosenberg
1 sibling, 0 replies; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-01 1:31 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
If the configuration option receive.fsckobjects is true or if the
application requests it on the Transport each object received over
the wire is validated to pass "git fsck" style rules. This can be
useful when fetching data from an untrusted source, to ensure that
the incoming objects comply with parsing standards.
The optional checking does require extra CPU on the client side. A
test against git.git (69601 objects 49719 deltas) showed:
receive.fsckobjects average time
--------------------------------------
false (default) 0m17.588s
true 0m18.465s
So the additional checking costs about 5% more in client side time.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../jgit/transport/BasePackFetchConnection.java | 1 +
.../src/org/spearce/jgit/transport/IndexPack.java | 60 ++++++++++++++++++--
.../src/org/spearce/jgit/transport/Transport.java | 24 ++++++++
.../spearce/jgit/transport/TransportBundle.java | 10 +++-
.../jgit/transport/WalkFetchConnection.java | 26 +++++++--
5 files changed, 108 insertions(+), 13 deletions(-)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index a22b33d..a542eb7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -452,6 +452,7 @@ private void receivePack(final ProgressMonitor monitor) throws IOException {
ip = IndexPack.create(local, sideband ? pckIn.sideband(monitor) : in);
ip.setFixThin(thinPack);
+ ip.setObjectChecking(transport.isCheckFetchedObjects());
ip.index(monitor);
ip.renameAndOpenPack();
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
index 8a66ec9..ef1ee52 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
@@ -56,10 +56,12 @@
import org.spearce.jgit.errors.CorruptObjectException;
import org.spearce.jgit.errors.MissingObjectException;
+import org.spearce.jgit.lib.AnyObjectId;
import org.spearce.jgit.lib.BinaryDelta;
import org.spearce.jgit.lib.Constants;
import org.spearce.jgit.lib.InflaterCache;
import org.spearce.jgit.lib.MutableObjectId;
+import org.spearce.jgit.lib.ObjectChecker;
import org.spearce.jgit.lib.ObjectId;
import org.spearce.jgit.lib.ObjectIdMap;
import org.spearce.jgit.lib.ObjectLoader;
@@ -136,6 +138,8 @@ public static IndexPack create(final Repository db, final InputStream is)
private int bAvail;
+ private ObjectChecker objCheck;
+
private boolean fixThin;
private int outputVersion;
@@ -231,6 +235,40 @@ public void setFixThin(final boolean fix) {
}
/**
+ * Configure the checker used to validate received objects.
+ * <p>
+ * Usually object checking isn't necessary, as Git implementations only
+ * create valid objects in pack files. However, additional checking may be
+ * useful if processing data from an untrusted source.
+ *
+ * @param oc
+ * the checker instance; null to disable object checking.
+ */
+ public void setObjectChecker(final ObjectChecker oc) {
+ objCheck = oc;
+ }
+
+ /**
+ * Configure the checker used to validate received objects.
+ * <p>
+ * Usually object checking isn't necessary, as Git implementations only
+ * create valid objects in pack files. However, additional checking may be
+ * useful if processing data from an untrusted source.
+ * <p>
+ * This is shorthand for:
+ *
+ * <pre>
+ * setObjectChecker(on ? new ObjectChecker() : null);
+ * </pre>
+ *
+ * @param on
+ * true to enable the default checker; false to disable it.
+ */
+ public void setObjectChecking(final boolean on) {
+ setObjectChecker(on ? new ObjectChecker() : null);
+ }
+
+ /**
* Consume data from the input stream until the packfile is indexed.
*
* @param progress
@@ -375,7 +413,7 @@ private void resolveDeltas(final long pos, final int oldCRC, int type,
objectDigest.update(data);
tempObjectId.fromRaw(objectDigest.digest(), 0);
- verifyNoCollision(type, data);
+ verifySafeObject(tempObjectId, type, data);
oe = new PackedObjectInfo(pos, crc32, tempObjectId);
entries[entryCount++] = oe;
}
@@ -658,18 +696,28 @@ private void whole(final int type, final long pos, final long sz)
objectDigest.update(data);
tempObjectId.fromRaw(objectDigest.digest(), 0);
- verifyNoCollision(type, data);
+ verifySafeObject(tempObjectId, type, data);
final int crc32 = (int) crc.getValue();
entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId);
}
- private void verifyNoCollision(final int type, final byte[] data)
- throws IOException {
- final ObjectLoader ldr = repo.openObject(tempObjectId);
+ private void verifySafeObject(final AnyObjectId id, final int type,
+ final byte[] data) throws IOException {
+ if (objCheck != null) {
+ try {
+ objCheck.check(type, data);
+ } catch (CorruptObjectException e) {
+ throw new IOException("Invalid "
+ + Constants.encodedTypeString(type) + " " + id.name()
+ + ":" + e.getMessage());
+ }
+ }
+
+ final ObjectLoader ldr = repo.openObject(id);
if (ldr != null) {
final byte[] existingData = ldr.getCachedBytes();
if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
- throw new IOException("collision in " + tempObjectId.name());
+ throw new IOException("Collision on " + id.name());
}
}
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index 7284b28..28700b7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -55,6 +55,7 @@
import org.spearce.jgit.lib.ProgressMonitor;
import org.spearce.jgit.lib.Ref;
import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.TransferConfig;
/**
* Connects two Git repositories together and copies objects between them.
@@ -351,6 +352,9 @@ private static String findTrackingRefName(final String remoteName,
/** Should push just check for operation result, not really push. */
private boolean dryRun;
+ /** Should an incoming (fetch) transfer validate objects? */
+ private boolean checkFetchedObjects;
+
/**
* Create a new transport instance.
*
@@ -363,8 +367,10 @@ private static String findTrackingRefName(final String remoteName,
* URI passed to {@link #open(Repository, URIish)}.
*/
protected Transport(final Repository local, final URIish uri) {
+ final TransferConfig tc = local.getConfig().getTransfer();
this.local = local;
this.uri = uri;
+ this.checkFetchedObjects = tc.isFsckObjects();
}
/**
@@ -444,6 +450,24 @@ public void setFetchThin(final boolean fetchThin) {
}
/**
+ * @return true if fetch will verify received objects are formatted
+ * correctly. Validating objects requires more CPU time on the
+ * client side of the connection.
+ */
+ public boolean isCheckFetchedObjects() {
+ return checkFetchedObjects;
+ }
+
+ /**
+ * @param check
+ * true to enable checking received objects; false to assume all
+ * received objects are valid.
+ */
+ public void setCheckFetchedObjects(final boolean check) {
+ checkFetchedObjects = check;
+ }
+
+ /**
* Default setting is: {@value RemoteConfig#DEFAULT_RECEIVE_PACK}
*
* @return remote executable providing receive-pack service for pack
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
index d43a18f..5b321a0 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
@@ -170,8 +170,7 @@ protected void doFetch(final ProgressMonitor monitor,
final Collection<Ref> want) throws TransportException {
verifyPrerequisites();
try {
- final IndexPack ip = IndexPack.create(local, bin);
- ip.setFixThin(true);
+ final IndexPack ip = newIndexPack();
ip.index(monitor);
ip.renameAndOpenPack();
} catch (IOException err) {
@@ -183,6 +182,13 @@ protected void doFetch(final ProgressMonitor monitor,
}
}
+ private IndexPack newIndexPack() throws IOException {
+ final IndexPack ip = IndexPack.create(local, bin);
+ ip.setFixThin(true);
+ ip.setObjectChecking(TransportBundle.this.isCheckFetchedObjects());
+ return ip;
+ }
+
private void verifyPrerequisites() throws TransportException {
if (prereqs.isEmpty())
return;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
index f309fe1..5638454 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
@@ -59,6 +59,7 @@
import org.spearce.jgit.lib.AnyObjectId;
import org.spearce.jgit.lib.Constants;
import org.spearce.jgit.lib.FileMode;
+import org.spearce.jgit.lib.ObjectChecker;
import org.spearce.jgit.lib.ObjectId;
import org.spearce.jgit.lib.PackIndex;
import org.spearce.jgit.lib.ProgressMonitor;
@@ -99,6 +100,9 @@
/** The repository this transport fetches into, or pushes out of. */
private final Repository local;
+ /** If not null the validator for received objects. */
+ private final ObjectChecker objCheck;
+
/**
* List of all remote repositories we may need to get objects out of.
* <p>
@@ -157,9 +161,9 @@
*/
private final HashMap<ObjectId, List<Throwable>> fetchErrors;
- WalkFetchConnection(final WalkTransport walkTransport,
- final WalkRemoteObjectDatabase w) {
- local = walkTransport.local;
+ WalkFetchConnection(final WalkTransport wt, final WalkRemoteObjectDatabase w) {
+ local = wt.local;
+ objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
remotes = new ArrayList<WalkRemoteObjectDatabase>();
remotes.add(w);
@@ -560,10 +564,21 @@ private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
throw e;
}
- if (!AnyObjectId.equals(id, uol.getId()))
+ if (!AnyObjectId.equals(id, uol.getId())) {
throw new TransportException("Incorrect hash for " + id.name()
+ "; computed " + uol.getId().name() + " as a "
- + uol.getType() + " from " + compressed.length + " bytes.");
+ + Constants.encodedTypeString(uol.getType()) + " from "
+ + compressed.length + " bytes.");
+ }
+ if (objCheck != null) {
+ try {
+ objCheck.check(uol.getType(), uol.getCachedBytes());
+ } catch (CorruptObjectException e) {
+ throw new TransportException("Invalid "
+ + Constants.encodedTypeString(uol.getType()) + " "
+ + id.name() + ":" + e.getMessage());
+ }
+ }
}
private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
@@ -811,6 +826,7 @@ void downloadPack(final ProgressMonitor monitor) throws IOException {
s = connection.open("pack/" + packName);
ip = IndexPack.create(local, s.in);
ip.setFixThin(false);
+ ip.setObjectChecker(objCheck);
ip.index(monitor);
ip.renameAndOpenPack();
}
--
1.6.0.2.569.g798a2a
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit
2008-10-01 1:31 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 5/5] Honor receive.fsckobjects during any fetch connection Shawn O. Pearce
@ 2008-10-01 20:54 ` Robin Rosenberg
2008-10-02 22:46 ` Shawn O. Pearce
1 sibling, 1 reply; 11+ messages in thread
From: Robin Rosenberg @ 2008-10-01 20:54 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
The repo configuration setup fails. I'll squash this in
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
@@ -534,6 +534,7 @@ public void create() {
add(e);
core = new CoreConfig(this);
+ transfer = new TransferConfig(this);
}
/**
i.e. initialize the transfer object when creating a new repo in junit tests.
I also noted we try to read ~/.gitconfig which may give us som headaches
later on.
-- robin
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit
2008-10-01 20:54 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Robin Rosenberg
@ 2008-10-02 22:46 ` Shawn O. Pearce
0 siblings, 0 replies; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-02 22:46 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
>
> The repo configuration setup fails. I'll squash this in
Whoops. Thanks.
> I also noted we try to read ~/.gitconfig which may give us som headaches
> later on.
Oy. Issue #42 on the issue tracker for it. We shouldn't be reading
that during the tests.
--
Shawn.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy
2008-10-01 1:31 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 3/5] Object validation tests for "jgit fsck" Shawn O. Pearce
@ 2008-10-09 21:46 ` Jonas Fonseca
2008-10-09 21:49 ` Shawn O. Pearce
1 sibling, 1 reply; 11+ messages in thread
From: Jonas Fonseca @ 2008-10-09 21:46 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Robin Rosenberg, git
On Wed, Oct 1, 2008 at 03:31, Shawn O. Pearce <spearce@spearce.org> wrote:
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> index 5282491..87e861f 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> @@ -105,7 +105,12 @@ protected void setId(final ObjectId id) {
> * @throws IOException
> * the object cannot be read.
> */
> - public abstract byte[] getBytes() throws IOException;
> + public final byte[] getBytes() throws IOException {
> + final byte[] data = getCachedBytes();
> + final byte[] copy = new byte[data.length];
> + System.arraycopy(data, 0, copy, 0, data.length);
> + return data;
> + }
If I understand correctly, shouldn't this return the copy variable?
--
Jonas Fonseca
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy
2008-10-09 21:46 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Jonas Fonseca
@ 2008-10-09 21:49 ` Shawn O. Pearce
2008-10-09 22:09 ` [JGIT PATCH] Fix ObjectLoader.getBytes to really return the created copy Jonas Fonseca
0 siblings, 1 reply; 11+ messages in thread
From: Shawn O. Pearce @ 2008-10-09 21:49 UTC (permalink / raw)
To: Jonas Fonseca; +Cc: Robin Rosenberg, git
Jonas Fonseca <jonas.fonseca@gmail.com> wrote:
> On Wed, Oct 1, 2008 at 03:31, Shawn O. Pearce <spearce@spearce.org> wrote:
> > diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> > index 5282491..87e861f 100644
> > --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> > +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
> > @@ -105,7 +105,12 @@ protected void setId(final ObjectId id) {
> > * @throws IOException
> > * the object cannot be read.
> > */
> > - public abstract byte[] getBytes() throws IOException;
> > + public final byte[] getBytes() throws IOException {
> > + final byte[] data = getCachedBytes();
> > + final byte[] copy = new byte[data.length];
> > + System.arraycopy(data, 0, copy, 0, data.length);
> > + return data;
> > + }
>
> If I understand correctly, shouldn't this return the copy variable?
F&@!#*@#&@!#*@!@!&#@#@*@!
Yes. :-)
I think its already committed to master too. Can you send a patch
along to fix, and point out how stupid I am?
--
Shawn.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [JGIT PATCH] Fix ObjectLoader.getBytes to really return the created copy
2008-10-09 21:49 ` Shawn O. Pearce
@ 2008-10-09 22:09 ` Jonas Fonseca
0 siblings, 0 replies; 11+ messages in thread
From: Jonas Fonseca @ 2008-10-09 22:09 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Jonas Fonseca, Robin Rosenberg, git
The bug was carried over from the original code in PackedObjectLoader by
commit 4c49ab5a4ec8555681ceabf17142a15bf90c2c24.
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
---
.../src/org/spearce/jgit/lib/ObjectLoader.java | 2 +-
Shawn O. Pearce <spearce@spearce.org> wrote Thu, Oct 09, 2008:
> I think its already committed to master too. Can you send a patch
> along to fix, and point out how stupid I am?
Yes, I noticed it has already been applied. I am lagging behind a bit.
Anyway, here is a trivial patch.
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
index 87e861f..8d745dd 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
@@ -109,7 +109,7 @@ protected void setId(final ObjectId id) {
final byte[] data = getCachedBytes();
final byte[] copy = new byte[data.length];
System.arraycopy(data, 0, copy, 0, data.length);
- return data;
+ return copy;
}
/**
--
1.6.0.2.665.g48ddf
--
Jonas Fonseca
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2008-10-09 22:10 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-01 1:31 [JGIT PATCH 0/5] Support receive.fsckobjects Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 1/5] Expose RawParseUtils.match to application callers Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 3/5] Object validation tests for "jgit fsck" Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Shawn O. Pearce
2008-10-01 1:31 ` [JGIT PATCH 5/5] Honor receive.fsckobjects during any fetch connection Shawn O. Pearce
2008-10-01 20:54 ` [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit Robin Rosenberg
2008-10-02 22:46 ` Shawn O. Pearce
2008-10-09 21:46 ` [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy Jonas Fonseca
2008-10-09 21:49 ` Shawn O. Pearce
2008-10-09 22:09 ` [JGIT PATCH] Fix ObjectLoader.getBytes to really return the created copy Jonas Fonseca
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).