From a33663fd4edce3585ff20c842f297e60e0e9af85 Mon Sep 17 00:00:00 2001 From: Hongkai Liu Date: Tue, 24 Jan 2017 14:08:25 -0500 Subject: [PATCH] Detect stale-file-handle error in causal chain Cover the case where the exception is wrapped up as a cause, e.g., PackIndex#open(File). Change-Id: I0df5b1e9c2ff886bdd84dee3658b6a50866699d1 Signed-off-by: Hongkai Liu --- .../org/eclipse/jgit/util/FileUtilsTest.java | 40 +++++++++++++++++++ .../storage/file/ObjectDirectory.java | 2 +- .../internal/storage/file/RefDirectory.java | 3 +- .../src/org/eclipse/jgit/util/FileUtils.java | 20 ++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java index 73d8038b9..109d0e6ee 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java @@ -50,10 +50,14 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.rmi.RemoteException; import java.util.regex.Matcher; +import javax.management.remote.JMXProviderException; + import org.eclipse.jgit.junit.JGitTestUtil; import org.junit.After; import org.junit.Assume; @@ -61,6 +65,17 @@ import org.junit.Before; import org.junit.Test; public class FileUtilsTest { + private static final String MSG = "Stale file handle"; + + private static final String SOME_ERROR_MSG = "some error message"; + + private static final IOException IO_EXCEPTION = new UnsupportedEncodingException( + MSG); + + private static final IOException IO_EXCEPTION_WITH_CAUSE = new RemoteException( + SOME_ERROR_MSG, + new JMXProviderException(SOME_ERROR_MSG, IO_EXCEPTION)); + private File trash; @Before @@ -541,4 +556,29 @@ public class FileUtilsTest { return path.replaceAll("/|\\\\", Matcher.quoteReplacement(File.separator)); } + + @Test + public void testIsStaleFileHandleWithDirectCause() throws Exception { + assertTrue(FileUtils.isStaleFileHandle(IO_EXCEPTION)); + } + + @Test + public void testIsStaleFileHandleWithIndirectCause() throws Exception { + assertFalse( + FileUtils.isStaleFileHandle(IO_EXCEPTION_WITH_CAUSE)); + } + + @Test + public void testIsStaleFileHandleInCausalChainWithDirectCause() + throws Exception { + assertTrue( + FileUtils.isStaleFileHandleInCausalChain(IO_EXCEPTION)); + } + + @Test + public void testIsStaleFileHandleInCausalChainWithIndirectCause() + throws Exception { + assertTrue(FileUtils + .isStaleFileHandleInCausalChain(IO_EXCEPTION_WITH_CAUSE)); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index eec7fb7c3..b73152222 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -577,7 +577,7 @@ public class ObjectDirectory extends FileObjectDatabase { warnTmpl = JGitText.get().packWasDeleted; } removePack(p); - } else if (FileUtils.isStaleFileHandle(e)) { + } else if (FileUtils.isStaleFileHandleInCausalChain(e)) { warnTmpl = JGitText.get().packHandleIsStale; removePack(p); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index e3d0d6162..023c08c77 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -798,7 +798,8 @@ public class RefDirectory extends RefDatabase { return new PackedRefList(parsePackedRefs(br), snapshot, ObjectId.fromRaw(digest.digest())); } catch (IOException e) { - if (FileUtils.isStaleFileHandle(e) && retries < maxStaleRetries) { + if (FileUtils.isStaleFileHandleInCausalChain(e) + && retries < maxStaleRetries) { if (LOG.isDebugEnabled()) { LOG.debug(MessageFormat.format( JGitText.get().packedRefsHandleIsStale, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java index c04dfa961..1f1d15b32 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -547,6 +547,26 @@ public class FileUtils { .matches("stale .*file .*handle"); //$NON-NLS-1$ } + /** + * Determine if a throwable or a cause in its causal chain is a Stale NFS + * File Handle + * + * @param throwable + * @return a boolean true if the throwable or a cause in its causal chain is + * a Stale NFS File Handle + * @since 4.7 + */ + public static boolean isStaleFileHandleInCausalChain(Throwable throwable) { + while (throwable != null) { + if (throwable instanceof IOException + && isStaleFileHandle((IOException) throwable)) { + return true; + } + throwable = throwable.getCause(); + } + return false; + } + /** * @param file * @return {@code true} if the passed file is a symbolic link