diff --git a/.mvn/maven.config b/.mvn/maven.config index ebbe28853..3944d880e 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1 +1 @@ --T 1C +-T1C diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java index 0fca65290..618ccc0a0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java @@ -17,19 +17,48 @@ import static org.junit.Assert.fail; import java.io.File; +import java.io.IOException; import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collection; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.junit.MockSystemReader; import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.SystemReader; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class DirCacheBasicTest extends RepositoryTestCase { + @Parameter(0) + public boolean skipHash; + + @Parameters(name = "skipHash: {0}") + public static Collection getSkipHashValues() { + return Arrays + .asList(new Boolean[][] { { Boolean.TRUE }, + { Boolean.FALSE } }); + } + + @Before + public void setup() throws IOException { + FileBasedConfig cfg = db.getConfig(); + cfg.setBoolean(ConfigConstants.CONFIG_INDEX_SECTION, null, + ConfigConstants.CONFIG_KEY_SKIPHASH, skipHash); + cfg.save(); + } + @Test public void testReadMissing_RealIndex() throws Exception { final File idx = new File(db.getDirectory(), "index"); diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index b2322725c..f45bd07bd 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -19,6 +19,12 @@ + + + + + + diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 03da61583..e56061223 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -40,6 +40,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.FileSnapshot; import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.internal.storage.io.NullMessageDigest; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config.ConfigEnum; @@ -327,6 +328,9 @@ public static DirCache lock(final File indexLocation, final FS fs, /** If we read this index from disk, the original format. */ private DirCacheVersion version; + /** Whether to skip computing and checking the index checksum */ + private boolean skipHash; + /** * Create a new in-core index representation. *

@@ -446,7 +450,8 @@ public void clear() { private void readFrom(InputStream inStream) throws IOException, CorruptObjectException { final BufferedInputStream in = new BufferedInputStream(inStream); - final MessageDigest md = Constants.newMessageDigest(); + readConfig(); + MessageDigest md = newMessageDigest(); // Read the index header and verify we understand it. // @@ -543,8 +548,12 @@ private void readFrom(InputStream inStream) throws IOException, } readIndexChecksum = md.digest(); - if (!Arrays.equals(readIndexChecksum, hdr)) { - throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch); + if (!(skipHash + || Arrays.equals(readIndexChecksum, hdr) + || Arrays.equals(NullMessageDigest.getInstance().digest(), + hdr))) { + throw new CorruptObjectException( + JGitText.get().DIRCChecksumMismatch); } } @@ -627,15 +636,9 @@ public void write() throws IOException { } void writeTo(File dir, OutputStream os) throws IOException { - final MessageDigest foot = Constants.newMessageDigest(); - final DigestOutputStream dos = new DigestOutputStream(os, foot); - - if (version == null && this.repository != null) { - // A new DirCache is being written. - DirCacheConfig config = repository.getConfig() - .get(DirCacheConfig::new); - version = config.getIndexVersion(); - } + readConfig(); + MessageDigest foot = newMessageDigest(); + DigestOutputStream dos = new DigestOutputStream(os, foot); if (version == null || version == DirCacheVersion.DIRC_VERSION_MINIMUM) { version = DirCacheVersion.DIRC_VERSION_MINIMUM; @@ -707,6 +710,22 @@ void writeTo(File dir, OutputStream os) throws IOException { os.close(); } + private void readConfig() { + if (version == null && this.repository != null) { + DirCacheConfig config = repository.getConfig() + .get(DirCacheConfig::new); + version = config.getIndexVersion(); + skipHash = config.isSkipHash(); + } + } + + private MessageDigest newMessageDigest() { + if (skipHash) { + return NullMessageDigest.getInstance(); + } + return Constants.newMessageDigest(); + } + /** * Commit this change and release the lock. *

@@ -1071,6 +1090,8 @@ private static class DirCacheConfig { private final DirCacheVersion indexVersion; + private final boolean skipHash; + public DirCacheConfig(Config cfg) { boolean manyFiles = cfg.getBoolean( ConfigConstants.CONFIG_FEATURE_SECTION, @@ -1080,11 +1101,16 @@ public DirCacheConfig(Config cfg) { ConfigConstants.CONFIG_KEY_VERSION, manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS : DirCacheVersion.DIRC_VERSION_EXTENDED); + skipHash = cfg.getBoolean(ConfigConstants.CONFIG_INDEX_SECTION, + ConfigConstants.CONFIG_KEY_SKIPHASH, false); } public DirCacheVersion getIndexVersion() { return indexVersion; } + public boolean isSkipHash() { + return skipHash; + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java new file mode 100644 index 000000000..ea5877fff --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023, SAP SE and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.storage.io; + +import java.security.MessageDigest; + +import org.eclipse.jgit.lib.Constants; + +/** + * Dummy message digest consisting of only null bytes with the length of an + * ObjectId. This class can be used to skip computing a real digest. + */ +public final class NullMessageDigest extends MessageDigest { + private static final byte[] digest = new byte[Constants.OBJECT_ID_LENGTH]; + + private static final NullMessageDigest INSTANCE = new NullMessageDigest(); + + /** + * Get the only instance of NullMessageDigest + * + * @return the only instance of NullMessageDigest + */ + public static MessageDigest getInstance() { + return INSTANCE; + } + + private NullMessageDigest() { + super("null"); //$NON-NLS-1$ + } + + @Override + protected void engineUpdate(byte input) { + // empty + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + // empty + } + + @Override + protected byte[] engineDigest() { + return digest; + } + + @Override + protected void engineReset() { + // empty + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 96f3198ce..5c48c7a08 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -448,6 +448,12 @@ public final class ConfigConstants { /** The "indexversion" key */ public static final String CONFIG_KEY_INDEXVERSION = "indexversion"; + /** + * The "skiphash" key + * @since 5.13.2 + */ + public static final String CONFIG_KEY_SKIPHASH = "skiphash"; + /** * The "hidedotfiles" key * @since 3.5