* 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
<?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> |
<?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> |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
<component id="org.eclipse.jgit" version="2"> | <component id="org.eclipse.jgit" version="2"> | ||||
<resource path="META-INF/MANIFEST.MF"> | <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_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> | </message_arguments> | ||||
</filter> | </filter> | ||||
</resource> | </resource> |
*/ | */ | ||||
public boolean lock() throws IOException { | public boolean lock() throws IOException { | ||||
FileUtils.mkdirs(lck.getParentFile(), true); | FileUtils.mkdirs(lck.getParentFile(), true); | ||||
if (lck.createNewFile()) { | |||||
if (FS.DETECTED.createNewFile(lck)) { | |||||
haveLck = true; | haveLck = true; | ||||
try { | try { | ||||
os = new FileOutputStream(lck); | os = new FileOutputStream(lck); |
import org.eclipse.jgit.errors.ObjectWritingException; | import org.eclipse.jgit.errors.ObjectWritingException; | ||||
import org.eclipse.jgit.events.RefsChangedEvent; | import org.eclipse.jgit.events.RefsChangedEvent; | ||||
import org.eclipse.jgit.internal.JGitText; | import org.eclipse.jgit.internal.JGitText; | ||||
import org.eclipse.jgit.lib.ConfigConstants; | |||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectIdRef; | import org.eclipse.jgit.lib.ObjectIdRef; | ||||
} | } | ||||
private PackedRefList getPackedRefs() throws IOException { | private PackedRefList getPackedRefs() throws IOException { | ||||
boolean trustFolderStat = getRepository().getConfig().getBoolean( | |||||
ConfigConstants.CONFIG_CORE_SECTION, | |||||
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); | |||||
final PackedRefList curList = packedRefs.get(); | final PackedRefList curList = packedRefs.get(); | ||||
if (!curList.snapshot.isModified(packedRefsFile)) | |||||
if (trustFolderStat && !curList.snapshot.isModified(packedRefsFile)) { | |||||
return curList; | return curList; | ||||
} | |||||
final PackedRefList newList = readPackedRefs(); | final PackedRefList newList = readPackedRefs(); | ||||
if (packedRefs.compareAndSet(curList, newList) | if (packedRefs.compareAndSet(curList, newList) | ||||
&& !curList.id.equals(newList.id)) | |||||
&& !curList.id.equals(newList.id)) { | |||||
modCnt.incrementAndGet(); | modCnt.incrementAndGet(); | ||||
} | |||||
return newList; | return newList; | ||||
} | } | ||||
*/ | */ | ||||
public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat"; | 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" | * The "noprefix" key in the "diff section" | ||||
* @since 3.0 | * @since 3.0 |
*/ | */ | ||||
public abstract boolean supportsExecute(); | 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 | * Does this operating system and JRE supports symbolic links. The | ||||
* capability to handle symbolic links is detected at runtime. | * capability to handle symbolic links is detected at runtime. | ||||
FileUtils.createSymLink(path, target); | 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)}. | * See {@link FileUtils#relativize(String, String)}. | ||||
* | * |
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.nio.file.Path; | import java.nio.file.Path; | ||||
import java.nio.file.Paths; | |||||
import java.nio.file.attribute.PosixFilePermission; | import java.nio.file.attribute.PosixFilePermission; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import org.eclipse.jgit.api.errors.JGitInternalException; | import org.eclipse.jgit.api.errors.JGitInternalException; | ||||
import org.eclipse.jgit.errors.CommandFailedException; | 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.Constants; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.storage.file.FileBasedConfig; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
private static final int DEFAULT_UMASK = 0022; | private static final int DEFAULT_UMASK = 0022; | ||||
private volatile int umask = -1; | private volatile int umask = -1; | ||||
private volatile boolean supportsUnixNLink = true; | |||||
private volatile Boolean supportsAtomicCreateNewFile; | |||||
/** Default constructor. */ | /** Default constructor. */ | ||||
protected FS_POSIX() { | protected FS_POSIX() { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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 | @Override | ||||
public FS newInstance() { | public FS newInstance() { | ||||
return new FS_POSIX(this); | return new FS_POSIX(this); | ||||
return hookPath.toFile(); | return hookPath.toFile(); | ||||
return null; | 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); | |||||
} | |||||
} | |||||
} | } |