Browse Source

Cleanup double stat update of symlinks in DirCacheCheckout

When writing a symlink the stat data should only be written once
into the DirCacheEntry, based on the symlink itself and not the
possibly resolved destination observed by java.io.File.

Refactor the code to handle symlinks and early return.  This
removes the risk the blob stat info update is used against a
newly checked out symlink.

Hoist the file length stat update immediately after writing
the file, before a rename. This eliminates any race caused by another
process updating the file length after the rename and having it to
fall into the racily clean path.

Change-Id: I978ad9719c018ce1cf26947efbabaa8b9dff2217
tags/v3.6.0.201412230720-r
Shawn Pearce 9 years ago
parent
commit
a606dc363d
1 changed files with 31 additions and 33 deletions
  1. 31
    33
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java

+ 31
- 33
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java View File

@@ -1233,42 +1233,40 @@ public class DirCacheCheckout {
fs.createSymLink(f, target);
entry.setLength(bytes.length);
entry.setLastModified(fs.lastModified(f));
} else {
File tmpFile = File.createTempFile(
"._" + f.getName(), null, parentDir); //$NON-NLS-1$
FileOutputStream rawChannel = new FileOutputStream(tmpFile);
OutputStream channel;
if (opt.getAutoCRLF() == AutoCRLF.TRUE)
channel = new AutoCRLFOutputStream(rawChannel);
else
channel = rawChannel;
try {
ol.copyTo(channel);
} finally {
channel.close();
}
if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
if (!fs.canExecute(tmpFile))
fs.setExecute(tmpFile, true);
} else {
if (fs.canExecute(tmpFile))
fs.setExecute(tmpFile, false);
}
}
try {
FileUtils.rename(tmpFile, f);
} catch (IOException e) {
throw new IOException(MessageFormat.format(
JGitText.get().renameFileFailed, tmpFile.getPath(),
f.getPath()));
return;
}

File tmpFile = File.createTempFile(
"._" + f.getName(), null, parentDir); //$NON-NLS-1$
OutputStream channel = new FileOutputStream(tmpFile);
if (opt.getAutoCRLF() == AutoCRLF.TRUE)
channel = new AutoCRLFOutputStream(channel);
try {
ol.copyTo(channel);
} finally {
channel.close();
}
entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE
? f.length() // AutoCRLF wants on-disk-size
: (int) ol.getSize());

if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
if (!fs.canExecute(tmpFile))
fs.setExecute(tmpFile, true);
} else {
if (fs.canExecute(tmpFile))
fs.setExecute(tmpFile, false);
}
}
try {
FileUtils.rename(tmpFile, f);
} catch (IOException e) {
throw new IOException(MessageFormat.format(
JGitText.get().renameFileFailed, tmpFile.getPath(),
f.getPath()));
}
entry.setLastModified(f.lastModified());
if (opt.getAutoCRLF() != AutoCRLF.FALSE)
entry.setLength(f.length()); // AutoCRLF wants on-disk-size
else
entry.setLength((int) ol.getSize());
}

private static void checkValidPath(CanonicalTreeParser t)

Loading…
Cancel
Save