diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2023-02-22 02:18:10 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2023-02-22 02:18:10 +0100 |
commit | 95240cefaceaf31c991b19777fe77806abf08ad3 (patch) | |
tree | 46d910cd13f27de299ba6f6c3620ded6dc8f5e77 | |
parent | c8683db55d79b06ca7bdd04651a6520eaa7b15d9 (diff) | |
parent | e92212a5a00c84f5e1dda0b44646ba90c147b4da (diff) | |
download | jgit-95240cefaceaf31c991b19777fe77806abf08ad3.tar.gz jgit-95240cefaceaf31c991b19777fe77806abf08ad3.zip |
Merge branch 'master' into stable-6.5
* master:
Use Java 11 ProcessHandle to get pid of the current process
UploadPack: use allow-any-sha1-in-want configuration
Acquire file lock "gc.pid" before running gc
Silence API errors introduced by 9424052f
Change-Id: I71c8ee39e0f3438cbc1992c24f9f6c33ece88c07
6 files changed, 224 insertions, 1 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java index 4ad7fa3149..0e97d7b155 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java @@ -2726,6 +2726,50 @@ public class UploadPackTest { assertEquals(1, stats.getNotAdvertisedWants()); } + @Test + public void testAllowAnySha1InWantConfig() { + server.getConfig().setBoolean("uploadpack", null, "allowanysha1inwant", + true); + + try (UploadPack uploadPack = new UploadPack(server)) { + assertEquals(RequestPolicy.ANY, uploadPack.getRequestPolicy()); + } + } + + @Test + public void testAllowReachableSha1InWantConfig() { + server.getConfig().setBoolean("uploadpack", null, + "allowreachablesha1inwant", true); + + try (UploadPack uploadPack = new UploadPack(server)) { + assertEquals(RequestPolicy.REACHABLE_COMMIT, + uploadPack.getRequestPolicy()); + } + } + + @Test + public void testAllowTipSha1InWantConfig() { + server.getConfig().setBoolean("uploadpack", null, "allowtipsha1inwant", + true); + + try (UploadPack uploadPack = new UploadPack(server)) { + assertEquals(RequestPolicy.TIP, uploadPack.getRequestPolicy()); + } + } + + @Test + public void testAllowReachableTipSha1InWantConfig() { + server.getConfig().setBoolean("uploadpack", null, + "allowreachablesha1inwant", true); + server.getConfig().setBoolean("uploadpack", null, "allowtipsha1inwant", + true); + + try (UploadPack uploadPack = new UploadPack(server)) { + assertEquals(RequestPolicy.REACHABLE_COMMIT_TIP, + uploadPack.getRequestPolicy()); + } + } + private class RefCallsCountingRepository extends InMemoryRepository { private final InMemoryRepository.MemRefDatabase refdb; private int numRefCalls; diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 72a3d1cdea..faea9cbea3 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -140,6 +140,7 @@ classCastNotA=Not a {0} cloneNonEmptyDirectory=Destination path "{0}" already exists and is not an empty directory closed=closed closeLockTokenFailed=Closing LockToken ''{0}'' failed +closePidLockFailed=Closing lock file ''{0}'' failed collisionOn=Collision on {0} commandClosedStderrButDidntExit=Command {0} closed stderr stream but didn''t exit within timeout {1} seconds commandRejectedByHook=Rejected by "{0}" hook.\n{1} @@ -323,6 +324,7 @@ expectedReceivedContentType=expected Content-Type {0}; received Content-Type {1} expectedReportForRefNotReceived={0}: expected report for ref {1} not received failedAtomicFileCreation=Atomic file creation failed, number of hard links to file {0} was not 2 but {1} failedCreateLockFile=Creating lock file {} failed +failedPidLock=Failed to lock ''{0}'' guarding git gc failedReadHttpsProtocols=Failed to read system property https.protocols, assuming it is not set failedToConvert=Failed to convert rest: %s failedToDetermineFilterDefinition=An exception occurred while determining filter definitions @@ -343,6 +345,7 @@ flagIsDisposed={0} is disposed. flagNotFromThis={0} not from this. flagsAlreadyCreated={0} flags already created. funnyRefname=funny refname +gcAlreadyRunning=fatal: gc is already running on machine ''{0}'' pid {1} gcFailed=Garbage collection failed. gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire. headRequiredToStash=HEAD required to stash local changes @@ -714,6 +717,7 @@ sslTrustAlways=Always skip SSL verification for this server from now on sslTrustForRepo=Skip SSL verification for git operations for repository {0} sslTrustNow=Skip SSL verification for this single git operation sslVerifyCannotSave=Could not save setting for http.sslVerify +stalePidLock=Lock file ''{0}'' is older than 12 hours and seems to be stale, lastModified: {1}, trying to lock it staleRevFlagsOn=Stale RevFlags on {0} startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported stashApplyConflict=Applying stashed changes resulted in a conflict diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index d8720be56f..39cc749cc4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -168,6 +168,7 @@ public class JGitText extends TranslationBundle { /***/ public String cloneNonEmptyDirectory; /***/ public String closeLockTokenFailed; /***/ public String closed; + /***/ public String closePidLockFailed; /***/ public String collisionOn; /***/ public String commandClosedStderrButDidntExit; /***/ public String commandRejectedByHook; @@ -351,6 +352,7 @@ public class JGitText extends TranslationBundle { /***/ public String expectedReportForRefNotReceived; /***/ public String failedAtomicFileCreation; /***/ public String failedCreateLockFile; + /***/ public String failedPidLock; /***/ public String failedReadHttpsProtocols; /***/ public String failedToDetermineFilterDefinition; /***/ public String failedToConvert; @@ -371,6 +373,7 @@ public class JGitText extends TranslationBundle { /***/ public String flagNotFromThis; /***/ public String flagsAlreadyCreated; /***/ public String funnyRefname; + /***/ public String gcAlreadyRunning; /***/ public String gcFailed; /***/ public String gcTooManyUnpruned; /***/ public String headRequiredToStash; @@ -742,6 +745,7 @@ public class JGitText extends TranslationBundle { /***/ public String sslTrustForRepo; /***/ public String sslTrustNow; /***/ public String sslVerifyCannotSave; + /***/ public String stalePidLock; /***/ public String staleRevFlagsOn; /***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported; /***/ public String stashApplyConflict; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index d41aea4fdd..111a4f301f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -21,9 +21,16 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.io.StringWriter; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -89,8 +96,11 @@ import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.LockToken; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.GitDateParser; +import org.eclipse.jgit.util.StringUtils; import org.eclipse.jgit.util.SystemReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -265,6 +275,10 @@ public class GC { if (automatic && !needGc()) { return Collections.emptyList(); } + try (PidLock lock = new PidLock()) { + if (!lock.lock()) { + return Collections.emptyList(); + } pm.start(6 /* tasks */); packRefs(); // TODO: implement reflog_expire(pm, repo); @@ -276,6 +290,7 @@ public class GC { } return newPacks; } + } /** * Loosen objects in a pack file which are not also in the newly-created @@ -1706,4 +1721,143 @@ public class GC { return repo.getConfig().getInt(ConfigConstants.CONFIG_GC_SECTION, ConfigConstants.CONFIG_KEY_AUTO, DEFAULT_AUTOLIMIT); } + + private class PidLock implements AutoCloseable { + + private static final String GC_PID = "gc.pid"; //$NON-NLS-1$ + + private final Path pidFile; + + private LockToken token; + + private FileLock lock; + + private RandomAccessFile f; + + private FileChannel channel; + + PidLock() { + pidFile = repo.getDirectory().toPath().resolve(GC_PID); + } + + boolean lock() { + if (Files.exists(pidFile)) { + Instant mtime = FS.DETECTED + .lastModifiedInstant(pidFile.toFile()); + Instant twelveHoursAgo = Instant.now().minus(12, + ChronoUnit.HOURS); + if (mtime.compareTo(twelveHoursAgo) > 0) { + gcAlreadyRunning(); + return false; + } + LOG.warn(MessageFormat.format(JGitText.get().stalePidLock, + pidFile, mtime)); + } + try { + token = FS.DETECTED.createNewFileAtomic(pidFile.toFile()); + f = new RandomAccessFile(pidFile.toFile(), "rw"); //$NON-NLS-1$ + channel = f.getChannel(); + lock = channel.tryLock(); + if (lock == null) { + failedToLock(); + return false; + } + channel.write(ByteBuffer + .wrap(getProcDesc().getBytes(StandardCharsets.UTF_8))); + Thread cleanupHook = new Thread(() -> close()); + try { + Runtime.getRuntime().addShutdownHook(cleanupHook); + } catch (IllegalStateException e) { + // ignore - the VM is already shutting down + } + } catch (IOException | OverlappingFileLockException e) { + try { + failedToLock(); + } catch (Exception e1) { + LOG.error( + MessageFormat.format( + JGitText.get().closePidLockFailed, pidFile), + e1); + } + return false; + } + return true; + } + + private void failedToLock() { + close(); + LOG.error(MessageFormat.format(JGitText.get().failedPidLock, + pidFile)); + } + + private void gcAlreadyRunning() { + close(); + try { + Optional<String> s = Files.lines(pidFile).findFirst(); + String machine = null; + String pid = null; + if (s.isPresent()) { + String[] c = s.get().split("\\s+"); //$NON-NLS-1$ + pid = c[0]; + machine = c[1]; + } + if (!StringUtils.isEmptyOrNull(machine) + && !StringUtils.isEmptyOrNull(pid)) { + LOG.error(MessageFormat.format( + JGitText.get().gcAlreadyRunning, machine, pid)); + return; + } + } catch (IOException e) { + // ignore + } + LOG.error(MessageFormat.format(JGitText.get().failedPidLock, + pidFile)); + } + + private String getProcDesc() { + StringBuffer s = new StringBuffer(Long.toString(getPID())); + s.append(' '); + s.append(getHostName()); + return s.toString(); + } + + private long getPID() { + return ProcessHandle.current().pid(); + } + + private String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + return ""; //$NON-NLS-1$ + } + } + + @Override + public void close() { + boolean wasLocked = false; + try { + if (lock != null) { + lock.release(); + wasLocked = true; + } + if (channel != null) { + channel.close(); + } + if (f != null) { + f.close(); + } + if (token != null) { + token.close(); + } + if (wasLocked) { + FileUtils.delete(pidFile.toFile(), FileUtils.RETRY); + } + } catch (IOException e) { + LOG.error(MessageFormat + .format(JGitText.get().closePidLockFailed, pidFile), e); + } + } + + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java index 805166a405..064201a629 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2020 Google Inc. and others + * Copyright (C) 2008, 2023 Google Inc. 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 @@ -118,6 +118,7 @@ public class TransferConfig { private final boolean allowRefInWant; private final boolean allowTipSha1InWant; private final boolean allowReachableSha1InWant; + private final boolean allowAnySha1InWant; private final boolean allowFilter; private final boolean allowSidebandAll; @@ -202,6 +203,8 @@ public class TransferConfig { "uploadpack", "allowtipsha1inwant", false); allowReachableSha1InWant = rc.getBoolean( "uploadpack", "allowreachablesha1inwant", false); + allowAnySha1InWant = rc.getBoolean("uploadpack", "allowanysha1inwant", + false); allowFilter = rc.getBoolean( "uploadpack", "allowfilter", false); protocolVersion = ProtocolVersion.parse(rc @@ -284,6 +287,16 @@ public class TransferConfig { } /** + * Whether to allow clients to request any SHA-1s + * + * @return allow clients to request any SHA-1s? + * @since 6.5 + */ + public boolean isAllowAnySha1InWant() { + return allowAnySha1InWant; + } + + /** * @return true if clients are allowed to specify a "filter" line * @since 5.0 */ 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 38c7cb94bb..b648706475 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -681,6 +681,10 @@ public class UploadPack implements Closeable { */ public void setTransferConfig(@Nullable TransferConfig tc) { this.transferConfig = tc != null ? tc : new TransferConfig(db); + if (transferConfig.isAllowAnySha1InWant()) { + setRequestPolicy(RequestPolicy.ANY); + return; + } if (transferConfig.isAllowTipSha1InWant()) { setRequestPolicy(transferConfig.isAllowReachableSha1InWant() ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP); |