Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit/.settings/.api_filters6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java13
6 files changed, 97 insertions, 13 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 1083663327..9fa97b4e25 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -66,5 +66,11 @@
<message_argument value="supportsAtomicCreateNewFile()"/>
</message_arguments>
</filter>
+ <filter id="1142947843">
+ <message_arguments>
+ <message_argument value="4.5.6"/>
+ <message_argument value="fileAttributes(File)"/>
+ </message_arguments>
+ </filter>
</resource>
</component>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
index 97f3b573d0..be49f780a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.File;
import java.io.IOException;
+import java.nio.file.attribute.BasicFileAttributes;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -70,13 +71,20 @@ import org.eclipse.jgit.util.FS;
*/
public class FileSnapshot {
/**
+ * An unknown file size.
+ *
+ * This value is used when a comparison needs to happen purely on the lastUpdate.
+ */
+ public static final long UNKNOWN_SIZE = -1;
+
+ /**
* A FileSnapshot that is considered to always be modified.
* <p>
* This instance is useful for application code that wants to lazily read a
* file, but only after {@link #isModified(File)} gets invoked. The returned
* snapshot contains only invalid status information.
*/
- public static final FileSnapshot DIRTY = new FileSnapshot(-1, -1);
+ public static final FileSnapshot DIRTY = new FileSnapshot(-1, -1, UNKNOWN_SIZE);
/**
* A FileSnapshot that is clean if the file does not exist.
@@ -85,7 +93,7 @@ public class FileSnapshot {
* file to be clean. {@link #isModified(File)} will return false if the file
* path does not exist.
*/
- public static final FileSnapshot MISSING_FILE = new FileSnapshot(0, 0) {
+ public static final FileSnapshot MISSING_FILE = new FileSnapshot(0, 0, 0) {
@Override
public boolean isModified(File path) {
return FS.DETECTED.exists(path);
@@ -105,12 +113,16 @@ public class FileSnapshot {
public static FileSnapshot save(File path) {
long read = System.currentTimeMillis();
long modified;
+ long size;
try {
- modified = FS.DETECTED.lastModified(path);
+ BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
+ modified = fileAttributes.lastModifiedTime().toMillis();
+ size = fileAttributes.size();
} catch (IOException e) {
modified = path.lastModified();
+ size = path.length();
}
- return new FileSnapshot(read, modified);
+ return new FileSnapshot(read, modified, size);
}
/**
@@ -126,7 +138,7 @@ public class FileSnapshot {
*/
public static FileSnapshot save(long modified) {
final long read = System.currentTimeMillis();
- return new FileSnapshot(read, modified);
+ return new FileSnapshot(read, modified, -1);
}
/** Last observed modification time of the path. */
@@ -138,10 +150,16 @@ public class FileSnapshot {
/** True once {@link #lastRead} is far later than {@link #lastModified}. */
private boolean cannotBeRacilyClean;
- private FileSnapshot(long read, long modified) {
+ /** Underlying file-system size in bytes.
+ *
+ * When set to {@link #UNKNOWN_SIZE} the size is not considered for modification checks. */
+ private final long size;
+
+ private FileSnapshot(long read, long modified, long size) {
this.lastRead = read;
this.lastModified = modified;
this.cannotBeRacilyClean = notRacyClean(read);
+ this.size = size;
}
/**
@@ -152,6 +170,13 @@ public class FileSnapshot {
}
/**
+ * @return file size in bytes of last snapshot update
+ */
+ public long size() {
+ return size;
+ }
+
+ /**
* Check if the path may have been modified since the snapshot was saved.
*
* @param path
@@ -160,12 +185,16 @@ public class FileSnapshot {
*/
public boolean isModified(File path) {
long currLastModified;
+ long currSize;
try {
- currLastModified = FS.DETECTED.lastModified(path);
+ BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
+ currLastModified = fileAttributes.lastModifiedTime().toMillis();
+ currSize = fileAttributes.size();
} catch (IOException e) {
currLastModified = path.lastModified();
+ currSize = path.length();
}
- return isModified(currLastModified);
+ return (currSize != UNKNOWN_SIZE && currSize != size) || isModified(currLastModified);
}
/**
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 00e39533b1..e1934a13df 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
@@ -841,13 +841,13 @@ public class ObjectDirectory extends FileObjectDatabase {
}
final String packName = base + PACK.getExtension();
+ final File packFile = new File(packDirectory, packName);
final PackFile oldPack = forReuse.remove(packName);
- if (oldPack != null) {
+ if (oldPack != null && oldPack.getFileSnapshot().isModified(packFile)) {
list.add(oldPack);
continue;
}
- final File packFile = new File(packDirectory, packName);
list.add(new PackFile(packFile, extensions));
foundNew = true;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index 0611d3e859..e7db11d08c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -131,6 +131,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
int packLastModified;
+ private FileSnapshot fileSnapshot;
+
private volatile boolean invalid;
private boolean invalidBitmap;
@@ -164,7 +166,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
*/
public PackFile(final File packFile, int extensions) {
this.packFile = packFile;
- this.packLastModified = (int) (packFile.lastModified() >> 10);
+ this.fileSnapshot = FileSnapshot.save(packFile);
+ this.packLastModified = (int) (fileSnapshot.lastModified() >> 10);
this.extensions = extensions;
// Multiply by 31 here so we can more directly combine with another
@@ -338,6 +341,16 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
return getReverseIdx().findObject(offset);
}
+ /**
+ * Return the @{@link FileSnapshot} associated to the underlying packfile
+ * that has been used when the object was created.
+ *
+ * @return the packfile @{@link FileSnapshot} that the object is loaded from.
+ */
+ FileSnapshot getFileSnapshot() {
+ return fileSnapshot;
+ }
+
private final byte[] decompress(final long position, final int sz,
final WindowCursor curs) throws IOException, DataFormatException {
byte[] dstbuf;
@@ -633,9 +646,10 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
}
private void doOpen() throws IOException {
+ if (invalid) {
+ throw new PackInvalidException(packFile);
+ }
try {
- if (invalid)
- throw new PackInvalidException(packFile);
synchronized (readLock) {
fd = new RandomAccessFile(packFile, "r"); //$NON-NLS-1$
length = fd.length();
@@ -691,6 +705,14 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
ByteArrayWindow read(final long pos, int size) throws IOException {
synchronized (readLock) {
+ if (invalid || fd == null) {
+ // Due to concurrency between a read and another packfile invalidation thread
+ // one thread could come up to this point and then fail with NPE.
+ // Detect the situation and throw a proper exception so that can be properly
+ // managed by the main packfile search loop and the Git client won't receive
+ // any failures.
+ throw new PackInvalidException(packFile);
+ }
if (length < pos + size)
size = (int) (length - pos);
final byte[] buf = new byte[size];
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 8d4e5e52af..183bf460c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -55,6 +55,7 @@ import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
@@ -421,6 +422,19 @@ public abstract class FS {
public abstract boolean retryFailedLockFileCommit();
/**
+ * Return all the attributes of a file, without following symbolic links.
+ *
+ * @param file
+ * @return {@link BasicFileAttributes} of the file
+ * @throws IOException in case of any I/O errors accessing the file
+ *
+ * @since 4.5.6
+ */
+ public BasicFileAttributes fileAttributes(File file) throws IOException {
+ return FileUtils.fileAttributes(file);
+ }
+
+ /**
* Determine the user's home directory (location where preferences are).
*
* @return the user's home directory; null if the user does not have one.
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 1f20e9700d..31cfe4245d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -587,6 +587,19 @@ public class FileUtils {
}
/**
+ * Return all the attributes of a file, without following symbolic links.
+ *
+ * @param file
+ * @return {@link BasicFileAttributes} of the file
+ * @throws IOException in case of any I/O errors accessing the file
+ *
+ * @since 4.5.6
+ */
+ static BasicFileAttributes fileAttributes(File file) throws IOException {
+ return Files.readAttributes(file.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ }
+
+ /**
* @param file
* @param time
* @throws IOException

Back to the top