aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2017-11-22 23:06:50 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2017-11-22 23:07:01 +0100
commit5f79ef0ba495dbcf03eb4923ae7ce039fc8ab4ec (patch)
tree360ab0538388336bc59e30f276590eb9693edfc9
parent53becf1f59437c3295622c7203db470afad5b8d4 (diff)
parent182d3b3dadeb729ac3c5a7c1b653f30506f38d8e (diff)
downloadjgit-5f79ef0ba495dbcf03eb4923ae7ce039fc8ab4ec.tar.gz
jgit-5f79ef0ba495dbcf03eb4923ae7ce039fc8ab4ec.zip
Merge branch 'stable-4.6' into stable-4.7
* stable-4.6: Silence boxing warning Prepare 4.5.5-SNAPSHOT builds JGit v4.5.4.201711221230-r Fix LockFile semantics when running on NFS Honor trustFolderStats also when reading packed-refs Prepare 4.5.4-SNAPSHOT builds JGit v4.5.3.201708160445-r Change-Id: I8f6bc09540727c6273d22775a9f9ca382a729c9b Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.jgit.lfs.server/.settings/.api_filters11
-rw-r--r--org.eclipse.jgit/.settings/.api_filters52
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java88
7 files changed, 158 insertions, 44 deletions
diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters
deleted file mode 100644
index b5f65e1e76..0000000000
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.lfs.server" version="2">
- <resource path="src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java" type="org.eclipse.jgit.lfs.server.LfsProtocolServlet">
- <filter comment="breaking API for implementors is ok in minor releases" id="336695337">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.lfs.server.LfsProtocolServlet"/>
- <message_argument value="getLargeFileRepository(LfsProtocolServlet.LfsRequest, String, String)"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 822f1215d5..c68b9a0935 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,59 +3,51 @@
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
- <message_argument value="4.7.1"/>
+ <message_argument value="4.7.2"/>
<message_argument value="4.7.0"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/api/ArchiveCommand.java" type="org.eclipse.jgit.api.ArchiveCommand$Format">
- <filter comment="OSGi semver allows to break implementors in minor releases" id="403804204">
+ <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
+ <filter id="336658481">
<message_arguments>
- <message_argument value="org.eclipse.jgit.api.ArchiveCommand.Format"/>
- <message_argument value="putEntry(T, ObjectId, String, FileMode, ObjectLoader)"/>
+ <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
+ <message_argument value="CONFIG_KEY_AUTODETACH"/>
</message_arguments>
</filter>
- </resource>
- <resource path="src/org/eclipse/jgit/errors/NoPackSignatureException.java" type="org.eclipse.jgit.errors.NoPackSignatureException">
- <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+ <filter id="336658481">
<message_arguments>
- <message_argument value="4.5"/>
- <message_argument value="4.7"/>
- <message_argument value="org.eclipse.jgit.errors.NoPackSignatureException"/>
+ <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
+ <message_argument value="CONFIG_KEY_LOGEXPIRY"/>
</message_arguments>
</filter>
- </resource>
- <resource path="src/org/eclipse/jgit/errors/UnsupportedPackIndexVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException">
- <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+ <filter id="336658481">
<message_arguments>
- <message_argument value="4.5"/>
- <message_argument value="4.7"/>
- <message_argument value="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException"/>
+ <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
+ <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
</message_arguments>
</filter>
- </resource>
- <resource path="src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackVersionException">
- <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+ <filter id="1141899266">
<message_arguments>
<message_argument value="4.5"/>
<message_argument value="4.7"/>
- <message_argument value="org.eclipse.jgit.errors.UnsupportedPackVersionException"/>
+ <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/lib/ObjectInserter.java" type="org.eclipse.jgit.lib.ObjectInserter">
- <filter comment="changed return type of digest() breaking implementors which is ok in minor releases according to OSGi semantic versioning" id="338792546">
+ <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
+ <filter id="1141899266">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.ObjectInserter"/>
- <message_argument value="digest()"/>
+ <message_argument value="4.5"/>
+ <message_argument value="4.7"/>
+ <message_argument value="createNewFile(File)"/>
</message_arguments>
</filter>
- </resource>
- <resource path="src/org/eclipse/jgit/transport/http/HttpConnection.java" type="org.eclipse.jgit.transport.http.HttpConnection">
- <filter comment="OSGi semantic versioning rules allow to break implementors in minor releases" id="403767336">
+ <filter id="1141899266">
<message_arguments>
- <message_argument value="org.eclipse.jgit.transport.http.HttpConnection"/>
- <message_argument value="HTTP_MOVED_PERM"/>
+ <message_argument value="4.5"/>
+ <message_argument value="4.7"/>
+ <message_argument value="supportsAtomicCreateNewFile()"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index d2fcacf3ea..5af93cf2c4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -168,7 +168,7 @@ public class LockFile {
*/
public boolean lock() throws IOException {
FileUtils.mkdirs(lck.getParentFile(), true);
- if (lck.createNewFile()) {
+ if (FS.DETECTED.createNewFile(lck)) {
haveLck = true;
try {
os = new FileOutputStream(lck);
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 24d51a5ea6..51701f6dd3 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
@@ -80,6 +80,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
@@ -767,14 +768,20 @@ public class RefDirectory extends RefDatabase {
}
private PackedRefList getPackedRefs() throws IOException {
+ boolean trustFolderStat = getRepository().getConfig().getBoolean(
+ ConfigConstants.CONFIG_CORE_SECTION,
+ ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+
final PackedRefList curList = packedRefs.get();
- if (!curList.snapshot.isModified(packedRefsFile))
+ if (trustFolderStat && !curList.snapshot.isModified(packedRefsFile)) {
return curList;
+ }
final PackedRefList newList = readPackedRefs();
if (packedRefs.compareAndSet(curList, newList)
- && !curList.id.equals(newList.id))
+ && !curList.id.equals(newList.id)) {
modCnt.incrementAndGet();
+ }
return newList;
}
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 189361b709..26181809ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -341,6 +341,13 @@ public class ConfigConstants {
public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat";
/**
+ * The "supportsAtomicFileCreation" key in the "core section"
+ *
+ * @since 4.5
+ */
+ public static final String CONFIG_KEY_SUPPORTSATOMICFILECREATION = "supportsatomicfilecreation";
+
+ /**
* The "noprefix" key in the "diff section"
* @since 3.0
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 68b71309b1..98e73216b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -236,6 +236,21 @@ public abstract class FS {
public abstract boolean supportsExecute();
/**
+ * Does this file system support atomic file creation via
+ * java.io.File#createNewFile()? In certain environments (e.g. on NFS) it is
+ * not guaranteed that when two file system clients run createNewFile() in
+ * parallel only one will succeed. In such cases both clients may think they
+ * created a new file.
+ *
+ * @return true if this implementation support atomic creation of new
+ * Files by {@link File#createNewFile()}
+ * @since 4.5
+ */
+ public boolean supportsAtomicCreateNewFile() {
+ return true;
+ }
+
+ /**
* Does this operating system and JRE supports symbolic links. The
* capability to handle symbolic links is detected at runtime.
*
@@ -777,6 +792,22 @@ public abstract class FS {
}
/**
+ * Create a new file. See {@link File#createNewFile()}. Subclasses of this
+ * class may take care to provide a safe implementation for this even if
+ * {@link #supportsAtomicCreateNewFile()} is <code>false</code>
+ *
+ * @param path
+ * the file to be created
+ * @return <code>true</code> if the file was created, <code>false</code> if
+ * the file already existed
+ * @throws IOException
+ * @since 4.5
+ */
+ public boolean createNewFile(File path) throws IOException {
+ return path.createNewFile();
+ }
+
+ /**
* See {@link FileUtils#relativize(String, String)}.
*
* @param base
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index 0780d2b560..76aa697764 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -50,6 +50,7 @@ import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,8 +59,11 @@ import java.util.Set;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.CommandFailedException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -74,6 +78,10 @@ public class FS_POSIX extends FS {
private static final int DEFAULT_UMASK = 0022;
private volatile int umask = -1;
+ private volatile boolean supportsUnixNLink = true;
+
+ private volatile Boolean supportsAtomicCreateNewFile;
+
/** Default constructor. */
protected FS_POSIX() {
}
@@ -91,6 +99,34 @@ public class FS_POSIX extends FS {
}
}
+ @SuppressWarnings("boxing")
+ private void determineAtomicFileCreationSupport() {
+ // @TODO: enhance SystemReader to support this without copying code
+ Boolean ret = getAtomicFileCreationSupportOption(
+ SystemReader.getInstance().openUserConfig(null, this));
+ if (ret == null && StringUtils.isEmptyOrNull(SystemReader.getInstance()
+ .getenv(Constants.GIT_CONFIG_NOSYSTEM_KEY))) {
+ ret = getAtomicFileCreationSupportOption(
+ SystemReader.getInstance().openSystemConfig(null, this));
+ }
+ supportsAtomicCreateNewFile = (ret == null) || ret;
+ }
+
+ private Boolean getAtomicFileCreationSupportOption(FileBasedConfig config) {
+ try {
+ config.load();
+ String value = config.getString(ConfigConstants.CONFIG_CORE_SECTION,
+ null,
+ ConfigConstants.CONFIG_KEY_SUPPORTSATOMICFILECREATION);
+ if (value == null) {
+ return null;
+ }
+ return Boolean.valueOf(StringUtils.toBoolean(value));
+ } catch (IOException | ConfigInvalidException e) {
+ return Boolean.TRUE;
+ }
+ }
+
@Override
public FS newInstance() {
return new FS_POSIX(this);
@@ -301,4 +337,56 @@ public class FS_POSIX extends FS {
return hookPath.toFile();
return null;
}
+
+ @Override
+ public boolean supportsAtomicCreateNewFile() {
+ if (supportsAtomicCreateNewFile == null) {
+ determineAtomicFileCreationSupport();
+ }
+ return supportsAtomicCreateNewFile.booleanValue();
+ }
+
+ @Override
+ @SuppressWarnings("boxing")
+ /**
+ * An implementation of the File#createNewFile() semantics which works also
+ * on NFS. If the config option
+ * {@code core.supportsAtomicCreateNewFile = true} (which is the default)
+ * then simply File#createNewFile() is called.
+ *
+ * But if {@code core.supportsAtomicCreateNewFile = false} then after
+ * successful creation of the lock file a hardlink to that lock file is
+ * created and the attribute nlink of the lock file is checked to be 2. If
+ * multiple clients manage to create the same lock file nlink would be
+ * greater than 2 showing the error.
+ *
+ * @see https://www.time-travellers.org/shane/papers/NFS_considered_harmful.html
+ * @since 4.5
+ */
+ public boolean createNewFile(File lock) throws IOException {
+ if (!lock.createNewFile()) {
+ return false;
+ }
+ if (supportsAtomicCreateNewFile() || !supportsUnixNLink) {
+ return true;
+ }
+ Path lockPath = lock.toPath();
+ Path link = Files.createLink(Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$
+ lockPath);
+ try {
+ Integer nlink = (Integer) (Files.getAttribute(lockPath,
+ "unix:nlink")); //$NON-NLS-1$
+ if (nlink != 2) {
+ LOG.warn("nlink of link to lock file {0} was not 2 but {1}", //$NON-NLS-1$
+ lock.getPath(), nlink);
+ return false;
+ }
+ return true;
+ } catch (UnsupportedOperationException | IllegalArgumentException e) {
+ supportsUnixNLink = false;
+ return true;
+ } finally {
+ Files.delete(link);
+ }
+ }
}