Skip to content

Commit d96eb8b

Browse files
committed
Merge branch 'stable-4.5' into stable-4.6
* stable-4.5: Prepare 4.5.7-SNAPSHOT builds JGit v4.5.6.201903121547-r Check for packfile validity and fd before reading Move throw of PackInvalidException outside the catch Use FileSnapshot to get lastModified on PackFile Include size when comparing FileSnapshot Do not reuse packfiles when changed on filesystem Silence API warnings for new API introduced for fixes Change-Id: I029e1797447e6729de68bd89d4d69b324dbb3f5f Signed-off-by: Matthias Sohn <[email protected]>
2 parents 2e951b0 + a47367e commit d96eb8b

File tree

6 files changed

+113
-13
lines changed

6 files changed

+113
-13
lines changed

org.eclipse.jgit/.settings/.api_filters

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,27 @@
6565
<message_argument value="supportsAtomicCreateNewFile()"/>
6666
</message_arguments>
6767
</filter>
68+
<filter id="924844039">
69+
<message_arguments>
70+
<message_argument value="4.5.6"/>
71+
<message_argument value="4.5.0"/>
72+
</message_arguments>
73+
</filter>
74+
</resource>
75+
<resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
76+
<filter id="336658481">
77+
<message_arguments>
78+
<message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
79+
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
80+
</message_arguments>
81+
</filter>
82+
</resource>
83+
<resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
84+
<filter id="1142947843">
85+
<message_arguments>
86+
<message_argument value="4.5.6"/>
87+
<message_argument value="fileAttributes(File)"/>
88+
</message_arguments>
89+
</filter>
6890
</resource>
6991
</component>

org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
import java.io.File;
4747
import java.io.IOException;
48+
import java.nio.file.attribute.BasicFileAttributes;
4849
import java.text.DateFormat;
4950
import java.text.SimpleDateFormat;
5051
import java.util.Date;
@@ -69,14 +70,21 @@
6970
* file is less than 3 seconds ago.
7071
*/
7172
public class FileSnapshot {
73+
/**
74+
* An unknown file size.
75+
*
76+
* This value is used when a comparison needs to happen purely on the lastUpdate.
77+
*/
78+
public static final long UNKNOWN_SIZE = -1;
79+
7280
/**
7381
* A FileSnapshot that is considered to always be modified.
7482
* <p>
7583
* This instance is useful for application code that wants to lazily read a
7684
* file, but only after {@link #isModified(File)} gets invoked. The returned
7785
* snapshot contains only invalid status information.
7886
*/
79-
public static final FileSnapshot DIRTY = new FileSnapshot(-1, -1);
87+
public static final FileSnapshot DIRTY = new FileSnapshot(-1, -1, UNKNOWN_SIZE);
8088

8189
/**
8290
* A FileSnapshot that is clean if the file does not exist.
@@ -85,7 +93,7 @@ public class FileSnapshot {
8593
* file to be clean. {@link #isModified(File)} will return false if the file
8694
* path does not exist.
8795
*/
88-
public static final FileSnapshot MISSING_FILE = new FileSnapshot(0, 0) {
96+
public static final FileSnapshot MISSING_FILE = new FileSnapshot(0, 0, 0) {
8997
@Override
9098
public boolean isModified(File path) {
9199
return FS.DETECTED.exists(path);
@@ -105,12 +113,16 @@ public boolean isModified(File path) {
105113
public static FileSnapshot save(File path) {
106114
long read = System.currentTimeMillis();
107115
long modified;
116+
long size;
108117
try {
109-
modified = FS.DETECTED.lastModified(path);
118+
BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
119+
modified = fileAttributes.lastModifiedTime().toMillis();
120+
size = fileAttributes.size();
110121
} catch (IOException e) {
111122
modified = path.lastModified();
123+
size = path.length();
112124
}
113-
return new FileSnapshot(read, modified);
125+
return new FileSnapshot(read, modified, size);
114126
}
115127

116128
/**
@@ -126,7 +138,7 @@ public static FileSnapshot save(File path) {
126138
*/
127139
public static FileSnapshot save(long modified) {
128140
final long read = System.currentTimeMillis();
129-
return new FileSnapshot(read, modified);
141+
return new FileSnapshot(read, modified, -1);
130142
}
131143

132144
/** Last observed modification time of the path. */
@@ -138,10 +150,16 @@ public static FileSnapshot save(long modified) {
138150
/** True once {@link #lastRead} is far later than {@link #lastModified}. */
139151
private boolean cannotBeRacilyClean;
140152

141-
private FileSnapshot(long read, long modified) {
153+
/** Underlying file-system size in bytes.
154+
*
155+
* When set to {@link #UNKNOWN_SIZE} the size is not considered for modification checks. */
156+
private final long size;
157+
158+
private FileSnapshot(long read, long modified, long size) {
142159
this.lastRead = read;
143160
this.lastModified = modified;
144161
this.cannotBeRacilyClean = notRacyClean(read);
162+
this.size = size;
145163
}
146164

147165
/**
@@ -151,6 +169,13 @@ public long lastModified() {
151169
return lastModified;
152170
}
153171

172+
/**
173+
* @return file size in bytes of last snapshot update
174+
*/
175+
public long size() {
176+
return size;
177+
}
178+
154179
/**
155180
* Check if the path may have been modified since the snapshot was saved.
156181
*
@@ -160,12 +185,16 @@ public long lastModified() {
160185
*/
161186
public boolean isModified(File path) {
162187
long currLastModified;
188+
long currSize;
163189
try {
164-
currLastModified = FS.DETECTED.lastModified(path);
190+
BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
191+
currLastModified = fileAttributes.lastModifiedTime().toMillis();
192+
currSize = fileAttributes.size();
165193
} catch (IOException e) {
166194
currLastModified = path.lastModified();
195+
currSize = path.length();
167196
}
168-
return isModified(currLastModified);
197+
return (currSize != UNKNOWN_SIZE && currSize != size) || isModified(currLastModified);
169198
}
170199

171200
/**

org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,13 +820,13 @@ private PackList scanPacksImpl(final PackList old) {
820820
}
821821

822822
final String packName = base + PACK.getExtension();
823+
final File packFile = new File(packDirectory, packName);
823824
final PackFile oldPack = forReuse.remove(packName);
824-
if (oldPack != null) {
825+
if (oldPack != null && oldPack.getFileSnapshot().isModified(packFile)) {
825826
list.add(oldPack);
826827
continue;
827828
}
828829

829-
final File packFile = new File(packDirectory, packName);
830830
list.add(new PackFile(packFile, extensions));
831831
foundNew = true;
832832
}

org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public int compare(final PackFile a, final PackFile b) {
129129

130130
int packLastModified;
131131

132+
private FileSnapshot fileSnapshot;
133+
132134
private volatile boolean invalid;
133135

134136
private boolean invalidBitmap;
@@ -162,7 +164,8 @@ public int compare(final PackFile a, final PackFile b) {
162164
*/
163165
public PackFile(final File packFile, int extensions) {
164166
this.packFile = packFile;
165-
this.packLastModified = (int) (packFile.lastModified() >> 10);
167+
this.fileSnapshot = FileSnapshot.save(packFile);
168+
this.packLastModified = (int) (fileSnapshot.lastModified() >> 10);
166169
this.extensions = extensions;
167170

168171
// Multiply by 31 here so we can more directly combine with another
@@ -335,6 +338,16 @@ ObjectId findObjectForOffset(final long offset) throws IOException {
335338
return getReverseIdx().findObject(offset);
336339
}
337340

341+
/**
342+
* Return the @{@link FileSnapshot} associated to the underlying packfile
343+
* that has been used when the object was created.
344+
*
345+
* @return the packfile @{@link FileSnapshot} that the object is loaded from.
346+
*/
347+
FileSnapshot getFileSnapshot() {
348+
return fileSnapshot;
349+
}
350+
338351
private final byte[] decompress(final long position, final int sz,
339352
final WindowCursor curs) throws IOException, DataFormatException {
340353
byte[] dstbuf;
@@ -630,9 +643,10 @@ synchronized boolean endWindowCache() {
630643
}
631644

632645
private void doOpen() throws IOException {
646+
if (invalid) {
647+
throw new PackInvalidException(packFile);
648+
}
633649
try {
634-
if (invalid)
635-
throw new PackInvalidException(packFile);
636650
synchronized (readLock) {
637651
fd = new RandomAccessFile(packFile, "r"); //$NON-NLS-1$
638652
length = fd.length();
@@ -688,6 +702,14 @@ private void doClose() {
688702

689703
ByteArrayWindow read(final long pos, int size) throws IOException {
690704
synchronized (readLock) {
705+
if (invalid || fd == null) {
706+
// Due to concurrency between a read and another packfile invalidation thread
707+
// one thread could come up to this point and then fail with NPE.
708+
// Detect the situation and throw a proper exception so that can be properly
709+
// managed by the main packfile search loop and the Git client won't receive
710+
// any failures.
711+
throw new PackInvalidException(packFile);
712+
}
691713
if (length < pos + size)
692714
size = (int) (length - pos);
693715
final byte[] buf = new byte[size];

org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.io.OutputStream;
5353
import java.io.PrintStream;
5454
import java.nio.charset.Charset;
55+
import java.nio.file.attribute.BasicFileAttributes;
5556
import java.security.AccessController;
5657
import java.security.PrivilegedAction;
5758
import java.text.MessageFormat;
@@ -417,6 +418,19 @@ public FS setUserHome(File path) {
417418
*/
418419
public abstract boolean retryFailedLockFileCommit();
419420

421+
/**
422+
* Return all the attributes of a file, without following symbolic links.
423+
*
424+
* @param file
425+
* @return {@link BasicFileAttributes} of the file
426+
* @throws IOException in case of any I/O errors accessing the file
427+
*
428+
* @since 4.5.6
429+
*/
430+
public BasicFileAttributes fileAttributes(File file) throws IOException {
431+
return FileUtils.fileAttributes(file);
432+
}
433+
420434
/**
421435
* Determine the user's home directory (location where preferences are).
422436
*

org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,19 @@ static long lastModified(File file) throws IOException {
564564
.toMillis();
565565
}
566566

567+
/**
568+
* Return all the attributes of a file, without following symbolic links.
569+
*
570+
* @param file
571+
* @return {@link BasicFileAttributes} of the file
572+
* @throws IOException in case of any I/O errors accessing the file
573+
*
574+
* @since 4.5.6
575+
*/
576+
static BasicFileAttributes fileAttributes(File file) throws IOException {
577+
return Files.readAttributes(file.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
578+
}
579+
567580
/**
568581
* @param file
569582
* @param time

0 commit comments

Comments
 (0)