Determine hard-linking and nlink support per FileStore

It's quite possible that JGit can use the hard-linking mechanism
for atomic file creation on some volumes but not on others.
Ultimately it depends on the file systems on the mounted volumes.

Cache the information per FileStore instead of using a single
global flag. Also catch FileSystemException, it may be thrown
if the operating system reports a failure. The previously caught
AccessDeniedException is a sub-class of FileSystemException.

Bug: 547332
Change-Id: I1ef672b3468b0be79e71674344f16f28f9d11ba1
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
Thomas Wolf 2019-05-18 23:48:54 +02:00 committed by Matthias Sohn
parent d7bd2e700c
commit 1609ce7593
1 changed files with 24 additions and 9 deletions

View File

@ -48,7 +48,8 @@
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileStore;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -57,9 +58,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.JGitInternalException;
@ -84,7 +87,7 @@ public class FS_POSIX extends FS {
private static final int DEFAULT_UMASK = 0022;
private volatile int umask = -1;
private volatile boolean supportsUnixNLink = true;
private static final Map<FileStore, Boolean> CAN_HARD_LINK = new ConcurrentHashMap<>();
private volatile AtomicFileCreation supportsAtomicCreateNewFile = AtomicFileCreation.UNDEFINED;
@ -388,12 +391,18 @@ public boolean createNewFile(File lock) throws IOException {
if (!lock.createNewFile()) {
return false;
}
if (supportsAtomicCreateNewFile() || !supportsUnixNLink) {
if (supportsAtomicCreateNewFile()) {
return true;
}
Path lockPath = lock.toPath();
Path link = null;
FileStore store = Files.getFileStore(lockPath);
try {
Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
s -> Boolean.TRUE);
if (canLink == Boolean.FALSE) {
return true;
}
link = Files.createLink(
Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$
lockPath);
@ -405,11 +414,11 @@ public boolean createNewFile(File lock) throws IOException {
nlink));
return false;
} else if (nlink < 2) {
supportsUnixNLink = false;
CAN_HARD_LINK.put(store, Boolean.FALSE);
}
return true;
} catch (UnsupportedOperationException | IllegalArgumentException e) {
supportsUnixNLink = false;
CAN_HARD_LINK.put(store, Boolean.FALSE);
return true;
} finally {
if (link != null) {
@ -448,12 +457,18 @@ public LockToken createNewFileAtomic(File file) throws IOException {
if (!file.createNewFile()) {
return token(false, null);
}
if (supportsAtomicCreateNewFile() || !supportsUnixNLink) {
if (supportsAtomicCreateNewFile()) {
return token(true, null);
}
Path link = null;
Path path = file.toPath();
FileStore store = Files.getFileStore(path);
try {
Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
s -> Boolean.TRUE);
if (canLink == Boolean.FALSE) {
return token(true, null);
}
link = Files.createLink(Paths.get(uniqueLinkPath(file)), path);
Integer nlink = (Integer) (Files.getAttribute(path,
"unix:nlink")); //$NON-NLS-1$
@ -462,12 +477,12 @@ public LockToken createNewFileAtomic(File file) throws IOException {
JGitText.get().failedAtomicFileCreation, path, nlink));
return token(false, link);
} else if (nlink.intValue() < 2) {
supportsUnixNLink = false;
CAN_HARD_LINK.put(store, Boolean.FALSE);
}
return token(true, link);
} catch (UnsupportedOperationException | IllegalArgumentException
| AccessDeniedException | SecurityException e) {
supportsUnixNLink = false;
| FileSystemException | SecurityException e) {
CAN_HARD_LINK.put(store, Boolean.FALSE);
return token(true, link);
}
}