Browse Source

Rename files using NIO2 atomic rename

Bug: 319233
Change-Id: I5137212f5cd3195a52f90ed5e4ce3cf194a13efd
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-4.3
Matthias Sohn 10 years ago committed by Andrey Loskutov
parent
commit
3fc93f8a56
  1. 9
      org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
  2. 11
      org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
  3. 10
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
  4. 53
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
  5. 70
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
  6. 19
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
  7. 14
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
  8. 23
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java

9
org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java

@ -47,6 +47,7 @@ import java.io.FileOutputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -141,9 +142,13 @@ public class ApplyCommand extends GitCommand<ApplyResult> {
case RENAME: case RENAME:
f = getFile(fh.getOldPath(), false); f = getFile(fh.getOldPath(), false);
File dest = getFile(fh.getNewPath(), false); File dest = getFile(fh.getNewPath(), false);
if (!f.renameTo(dest)) try {
FileUtils.rename(f, dest,
StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
throw new PatchApplyException(MessageFormat.format( throw new PatchApplyException(MessageFormat.format(
JGitText.get().renameFileFailed, f, dest)); JGitText.get().renameFileFailed, f, dest), e);
}
break; break;
case COPY: case COPY:
f = getFile(fh.getOldPath(), false); f = getFile(fh.getOldPath(), false);

11
org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java

@ -46,6 +46,7 @@ import static org.eclipse.jgit.lib.Constants.R_STASH;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List; import java.util.List;
@ -220,12 +221,14 @@ public class StashDropCommand extends GitCommand<ObjectId> {
entry.getWho(), entry.getComment()); entry.getWho(), entry.getComment());
entryId = entry.getNewId(); entryId = entry.getNewId();
} }
if (!stashLockFile.renameTo(stashFile)) { try {
FileUtils.delete(stashFile); FileUtils.rename(stashLockFile, stashFile,
if (!stashLockFile.renameTo(stashFile)) StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format( throw new JGitInternalException(MessageFormat.format(
JGitText.get().renameFileFailed, JGitText.get().renameFileFailed,
stashLockFile.getPath(), stashFile.getPath())); stashLockFile.getPath(), stashFile.getPath()),
e);
} }
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashDropFailed, e); throw new JGitInternalException(JGitText.get().stashDropFailed, e);

10
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java vendored

@ -46,6 +46,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -1319,11 +1320,12 @@ public class DirCacheCheckout {
if (deleteRecursive && f.isDirectory()) { if (deleteRecursive && f.isDirectory()) {
FileUtils.delete(f, FileUtils.RECURSIVE); FileUtils.delete(f, FileUtils.RECURSIVE);
} }
FileUtils.rename(tmpFile, f); FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) { } catch (IOException e) {
throw new IOException(MessageFormat.format( throw new IOException(
JGitText.get().renameFileFailed, tmpFile.getPath(), MessageFormat.format(JGitText.get().renameFileFailed,
f.getPath())); tmpFile.getPath(), f.getPath()),
e);
} finally { } finally {
if (tmpFile.exists()) { if (tmpFile.exists()) {
FileUtils.delete(tmpFile); FileUtils.delete(tmpFile);

53
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java

@ -53,6 +53,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
@ -789,39 +790,33 @@ public class GC {
break; break;
} }
tmpPack.setReadOnly(); tmpPack.setReadOnly();
boolean delete = true;
try {
FileUtils.rename(tmpPack, realPack);
delete = false;
for (Map.Entry<PackExt, File> tmpEntry : tmpExts.entrySet()) {
File tmpExt = tmpEntry.getValue();
tmpExt.setReadOnly();
File realExt = nameFor(
id, "." + tmpEntry.getKey().getExtension()); //$NON-NLS-1$
try {
FileUtils.rename(tmpExt, realExt);
} catch (IOException e) {
File newExt = new File(realExt.getParentFile(),
realExt.getName() + ".new"); //$NON-NLS-1$
if (!tmpExt.renameTo(newExt))
newExt = tmpExt;
throw new IOException(MessageFormat.format(
JGitText.get().panicCantRenameIndexFile, newExt,
realExt));
}
}
} finally { FileUtils.rename(tmpPack, realPack, StandardCopyOption.ATOMIC_MOVE);
if (delete) { for (Map.Entry<PackExt, File> tmpEntry : tmpExts.entrySet()) {
if (tmpPack.exists()) File tmpExt = tmpEntry.getValue();
tmpPack.delete(); tmpExt.setReadOnly();
for (File tmpExt : tmpExts.values()) {
if (tmpExt.exists()) File realExt = nameFor(id,
tmpExt.delete(); "." + tmpEntry.getKey().getExtension()); //$NON-NLS-1$
try {
FileUtils.rename(tmpExt, realExt,
StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
File newExt = new File(realExt.getParentFile(),
realExt.getName() + ".new"); //$NON-NLS-1$
try {
FileUtils.rename(tmpExt, newExt,
StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e2) {
newExt = tmpExt;
e = e2;
} }
throw new IOException(MessageFormat.format(
JGitText.get().panicCantRenameIndexFile, newExt,
realExt), e);
} }
} }
return repo.getObjectDatabase().openPack(realPack); return repo.getObjectDatabase().openPack(realPack);
} finally { } finally {
if (tmpPack != null && tmpPack.exists()) if (tmpPack != null && tmpPack.exists())

70
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java

@ -54,6 +54,7 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import org.eclipse.jgit.errors.LockFailedException; import org.eclipse.jgit.errors.LockFailedException;
@ -128,8 +129,6 @@ public class LockFile {
private FileSnapshot commitSnapshot; private FileSnapshot commitSnapshot;
private final FS fs;
/** /**
* Create a new lock for any file. * Create a new lock for any file.
* *
@ -138,11 +137,24 @@ public class LockFile {
* @param fs * @param fs
* the file system abstraction which will be necessary to perform * the file system abstraction which will be necessary to perform
* certain file system operations. * certain file system operations.
* @deprecated use {@link LockFile#LockFile(File)} instead
*/ */
@Deprecated
public LockFile(final File f, final FS fs) { public LockFile(final File f, final FS fs) {
ref = f; ref = f;
lck = getLockFile(ref); lck = getLockFile(ref);
this.fs = fs; }
/**
* Create a new lock for any file.
*
* @param f
* the file that will be locked.
* @since 4.2
*/
public LockFile(final File f) {
ref = f;
lck = getLockFile(ref);
} }
/** /**
@ -441,56 +453,14 @@ public class LockFile {
} }
saveStatInformation(); saveStatInformation();
if (lck.renameTo(ref)) { try {
FileUtils.rename(lck, ref, StandardCopyOption.ATOMIC_MOVE);
haveLck = false; haveLck = false;
return true; return true;
} catch (IOException e) {
unlock();
return false;
} }
if (!ref.exists() || deleteRef()) {
if (renameLock()) {
haveLck = false;
return true;
}
}
unlock();
return false;
}
private boolean deleteRef() {
if (!fs.retryFailedLockFileCommit())
return ref.delete();
// File deletion fails on windows if another thread is
// concurrently reading the same file. So try a few times.
//
for (int attempts = 0; attempts < 10; attempts++) {
if (ref.delete())
return true;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return false;
}
}
return false;
}
private boolean renameLock() {
if (!fs.retryFailedLockFileCommit())
return lck.renameTo(ref);
// File renaming fails on windows if another thread is
// concurrently reading the same file. So try a few times.
//
for (int attempts = 0; attempts < 10; attempts++) {
if (lck.renameTo(ref))
return true;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return false;
}
}
return false;
} }
private void saveStatInformation() { private void saveStatInformation() {

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

@ -52,6 +52,9 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -608,10 +611,16 @@ public class ObjectDirectory extends FileObjectDatabase {
FileUtils.delete(tmp, FileUtils.RETRY); FileUtils.delete(tmp, FileUtils.RETRY);
return InsertLooseObjectResult.EXISTS_LOOSE; return InsertLooseObjectResult.EXISTS_LOOSE;
} }
if (tmp.renameTo(dst)) { try {
Files.move(tmp.toPath(), dst.toPath(),
StandardCopyOption.ATOMIC_MOVE);
dst.setReadOnly(); dst.setReadOnly();
unpackedObjectCache.add(id); unpackedObjectCache.add(id);
return InsertLooseObjectResult.INSERTED; return InsertLooseObjectResult.INSERTED;
} catch (AtomicMoveNotSupportedException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
// ignore
} }
// Maybe the directory doesn't exist yet as the object // Maybe the directory doesn't exist yet as the object
@ -619,10 +628,16 @@ public class ObjectDirectory extends FileObjectDatabase {
// try the rename first as the directory likely does exist. // try the rename first as the directory likely does exist.
// //
FileUtils.mkdir(dst.getParentFile(), true); FileUtils.mkdir(dst.getParentFile(), true);
if (tmp.renameTo(dst)) { try {
Files.move(tmp.toPath(), dst.toPath(),
StandardCopyOption.ATOMIC_MOVE);
dst.setReadOnly(); dst.setReadOnly();
unpackedObjectCache.add(id); unpackedObjectCache.add(id);
return InsertLooseObjectResult.INSERTED; return InsertLooseObjectResult.INSERTED;
} catch (AtomicMoveNotSupportedException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
LOG.debug(e.getMessage(), e);
} }
if (!createDuplicate && has(id)) { if (!createDuplicate && has(id)) {

14
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java

@ -50,6 +50,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
@ -476,20 +477,25 @@ public class ObjectDirectoryPackParser extends PackParser {
} }
} }
if (!tmpPack.renameTo(finalPack)) { try {
FileUtils.rename(tmpPack, finalPack,
StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
cleanupTemporaryFiles(); cleanupTemporaryFiles();
keep.unlock(); keep.unlock();
throw new IOException(MessageFormat.format( throw new IOException(MessageFormat.format(
JGitText.get().cannotMovePackTo, finalPack)); JGitText.get().cannotMovePackTo, finalPack), e);
} }
if (!tmpIdx.renameTo(finalIdx)) { try {
FileUtils.rename(tmpIdx, finalIdx, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
cleanupTemporaryFiles(); cleanupTemporaryFiles();
keep.unlock(); keep.unlock();
if (!finalPack.delete()) if (!finalPack.delete())
finalPack.deleteOnExit(); finalPack.deleteOnExit();
throw new IOException(MessageFormat.format( throw new IOException(MessageFormat.format(
JGitText.get().cannotMoveIndexTo, finalIdx)); JGitText.get().cannotMoveIndexTo, finalIdx), e);
} }
try { try {

23
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java

@ -46,6 +46,8 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.StandardCopyOption;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@ -54,6 +56,8 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Rename any reference stored by {@link RefDirectory}. * Rename any reference stored by {@link RefDirectory}.
@ -66,6 +70,9 @@ import org.eclipse.jgit.util.FileUtils;
* directory that happens to match the source name. * directory that happens to match the source name.
*/ */
class RefDirectoryRename extends RefRename { class RefDirectoryRename extends RefRename {
private static final Logger LOG = LoggerFactory
.getLogger(RefDirectoryRename.class);
private final RefDirectory refdb; private final RefDirectory refdb;
/** /**
@ -201,13 +208,25 @@ class RefDirectoryRename extends RefRename {
} }
private static boolean rename(File src, File dst) { private static boolean rename(File src, File dst) {
if (src.renameTo(dst)) try {
FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
return true; return true;
} catch (AtomicMoveNotSupportedException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
// ignore
}
File dir = dst.getParentFile(); File dir = dst.getParentFile();
if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory()) if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory())
return false; return false;
return src.renameTo(dst); try {
FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
return true;
} catch (IOException e) {
LOG.error(e.getMessage(), e);
return false;
}
} }
private boolean linkHEAD(RefUpdate target) { private boolean linkHEAD(RefUpdate target) {

Loading…
Cancel
Save