* stable-4.5: 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: Ie9c8e0d9172c8d53f075c284bf2a9677980d8dfb Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v4.9.1.201712030800-r
@@ -1,17 +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 implementors only which is ok under OSGi semver rules" id="336695337"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lfs.server.LfsProtocolServlet"/> | |||
<message_argument value="getLargeFileRepository(LfsProtocolServlet.LfsRequest, String)"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="breaking implementors only which is ok under OSGi semver rules" id="338792546"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lfs.server.LfsProtocolServlet"/> | |||
<message_argument value="getLargeFileRepository()"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
</component> |
@@ -1,17 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<component id="org.eclipse.jgit.lfs" version="2"> | |||
<resource path="src/org/eclipse/jgit/lfs/lib/Constants.java" type="org.eclipse.jgit.lfs.lib.Constants"> | |||
<filter id="388100214"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lfs.lib.Constants"/> | |||
<message_argument value="CONTENT_TYPE_GIT_LFS_JSON"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="388100214"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lfs.lib.Constants"/> | |||
<message_argument value="HDR_APPLICATION_OCTET_STREAM"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
</component> |
@@ -1,10 +1,68 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<component id="org.eclipse.jgit" version="2"> | |||
<resource path="META-INF/MANIFEST.MF"> | |||
<filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="924844039"> | |||
<filter id="924844039"> | |||
<message_arguments> | |||
<message_argument value="4.5.2"/> | |||
<message_argument value="4.5.0"/> | |||
<message_argument value="4.6.2"/> | |||
<message_argument value="4.6.0"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/errors/NoPackSignatureException.java" type="org.eclipse.jgit.errors.NoPackSignatureException"> | |||
<filter id="1108344834"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="org.eclipse.jgit.errors.NoPackSignatureException"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/errors/UnsupportedPackIndexVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException"> | |||
<filter id="1108344834"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackVersionException"> | |||
<filter id="1108344834"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="org.eclipse.jgit.errors.UnsupportedPackVersionException"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<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.lib.ConfigConstants"/> | |||
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS"> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="createNewFile(File)"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.6"/> | |||
<message_argument value="supportsAtomicCreateNewFile()"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> |
@@ -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); |
@@ -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; | |||
@@ -765,14 +766,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; | |||
} | |||
@@ -326,6 +326,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 |
@@ -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. | |||
@@ -776,6 +791,22 @@ public abstract class FS { | |||
FileUtils.createSymLink(path, target); | |||
} | |||
/** | |||
* 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)}. | |||
* |
@@ -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,33 @@ public class FS_POSIX extends FS { | |||
} | |||
} | |||
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 +336,55 @@ public class FS_POSIX extends FS { | |||
return hookPath.toFile(); | |||
return null; | |||
} | |||
@Override | |||
public boolean supportsAtomicCreateNewFile() { | |||
if (supportsAtomicCreateNewFile == null) { | |||
determineAtomicFileCreationSupport(); | |||
} | |||
return supportsAtomicCreateNewFile.booleanValue(); | |||
} | |||
@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); | |||
} | |||
} | |||
} |