* stable-5.7: ObjectDirectoryInserter: Open FileOutputStream in try-with-resource ObjectDirectoryInserter: Remove redundant 'throws' declarations ObjectDirectory: Further clean up insertUnpackedObject ObjectDirectory: Explicitly handle NoSuchFileException ObjectDirectory: Fail immediately when atomic move is not supported Change-Id: I05186baa517388680fcc6825c940c4c772f26d32 Signed-off-by: David Pursehouse <david.pursehouse@gmail.com>tags/v5.8.0.202006091008-r
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.file.AtomicMoveNotSupportedException; | |||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
import java.nio.file.NoSuchFileException; | |||||
import java.nio.file.StandardCopyOption; | import java.nio.file.StandardCopyOption; | ||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
FileUtils.delete(tmp, FileUtils.RETRY); | FileUtils.delete(tmp, FileUtils.RETRY); | ||||
return InsertLooseObjectResult.EXISTS_LOOSE; | return InsertLooseObjectResult.EXISTS_LOOSE; | ||||
} | } | ||||
try { | try { | ||||
Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst), | |||||
StandardCopyOption.ATOMIC_MOVE); | |||||
dst.setReadOnly(); | |||||
unpackedObjectCache.add(id); | |||||
return InsertLooseObjectResult.INSERTED; | |||||
} catch (AtomicMoveNotSupportedException e) { | |||||
LOG.error(e.getMessage(), e); | |||||
return tryMove(tmp, dst, id); | |||||
} catch (NoSuchFileException e) { | |||||
// It's possible the directory doesn't exist yet as the object | |||||
// directories are always lazily created. Note that we try the | |||||
// rename/move first as the directory likely does exist. | |||||
// | |||||
// Create the directory. | |||||
// | |||||
FileUtils.mkdir(dst.getParentFile(), true); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
// ignore | |||||
// Any other IO error is considered a failure. | |||||
// | |||||
LOG.error(e.getMessage(), e); | |||||
FileUtils.delete(tmp, FileUtils.RETRY); | |||||
return InsertLooseObjectResult.FAILURE; | |||||
} | } | ||||
// Maybe the directory doesn't exist yet as the object | |||||
// directories are always lazily created. Note that we | |||||
// try the rename first as the directory likely does exist. | |||||
// | |||||
FileUtils.mkdir(dst.getParentFile(), true); | |||||
try { | try { | ||||
Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst), | |||||
StandardCopyOption.ATOMIC_MOVE); | |||||
dst.setReadOnly(); | |||||
unpackedObjectCache.add(id); | |||||
return InsertLooseObjectResult.INSERTED; | |||||
} catch (AtomicMoveNotSupportedException e) { | |||||
LOG.error(e.getMessage(), e); | |||||
return tryMove(tmp, dst, id); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
LOG.debug(e.getMessage(), e); | |||||
} | |||||
if (!createDuplicate && has(id)) { | |||||
// The object failed to be renamed into its proper location and | |||||
// it doesn't exist in the repository either. We really don't | |||||
// know what went wrong, so fail. | |||||
// | |||||
LOG.error(e.getMessage(), e); | |||||
FileUtils.delete(tmp, FileUtils.RETRY); | FileUtils.delete(tmp, FileUtils.RETRY); | ||||
return InsertLooseObjectResult.EXISTS_PACKED; | |||||
return InsertLooseObjectResult.FAILURE; | |||||
} | } | ||||
} | |||||
// The object failed to be renamed into its proper | |||||
// location and it doesn't exist in the repository | |||||
// either. We really don't know what went wrong, so | |||||
// fail. | |||||
// | |||||
FileUtils.delete(tmp, FileUtils.RETRY); | |||||
return InsertLooseObjectResult.FAILURE; | |||||
private InsertLooseObjectResult tryMove(File tmp, File dst, | |||||
ObjectId id) | |||||
throws IOException { | |||||
Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst), | |||||
StandardCopyOption.ATOMIC_MOVE); | |||||
dst.setReadOnly(); | |||||
unpackedObjectCache.add(id); | |||||
return InsertLooseObjectResult.INSERTED; | |||||
} | } | ||||
boolean searchPacksAgain(PackList old) { | boolean searchPacksAgain(PackList old) { |
import java.io.EOFException; | import java.io.EOFException; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileNotFoundException; | |||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.FilterOutputStream; | import java.io.FilterOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
private ObjectId insertOneObject( | private ObjectId insertOneObject( | ||||
File tmp, ObjectId id, boolean createDuplicate) | File tmp, ObjectId id, boolean createDuplicate) | ||||
throws IOException, ObjectWritingException { | |||||
throws IOException { | |||||
switch (db.insertUnpackedObject(tmp, id, createDuplicate)) { | switch (db.insertUnpackedObject(tmp, id, createDuplicate)) { | ||||
case INSERTED: | case INSERTED: | ||||
case EXISTS_PACKED: | case EXISTS_PACKED: | ||||
} | } | ||||
} | } | ||||
@SuppressWarnings("resource" /* java 7 */) | |||||
private File toTemp(final SHA1 md, final int type, long len, | private File toTemp(final SHA1 md, final int type, long len, | ||||
final InputStream is) throws IOException, FileNotFoundException, | |||||
Error { | |||||
final InputStream is) throws IOException { | |||||
boolean delete = true; | boolean delete = true; | ||||
File tmp = newTempFile(); | File tmp = newTempFile(); | ||||
try { | |||||
FileOutputStream fOut = new FileOutputStream(tmp); | |||||
try (FileOutputStream fOut = new FileOutputStream(tmp)) { | |||||
try { | try { | ||||
OutputStream out = fOut; | OutputStream out = fOut; | ||||
if (config.getFSyncObjectFiles()) | |||||
if (config.getFSyncObjectFiles()) { | |||||
out = Channels.newOutputStream(fOut.getChannel()); | out = Channels.newOutputStream(fOut.getChannel()); | ||||
} | |||||
DeflaterOutputStream cOut = compress(out); | DeflaterOutputStream cOut = compress(out); | ||||
SHA1OutputStream dOut = new SHA1OutputStream(cOut, md); | SHA1OutputStream dOut = new SHA1OutputStream(cOut, md); | ||||
writeHeader(dOut, type, len); | writeHeader(dOut, type, len); | ||||
final byte[] buf = buffer(); | final byte[] buf = buffer(); | ||||
while (len > 0) { | while (len > 0) { | ||||
int n = is.read(buf, 0, (int) Math.min(len, buf.length)); | int n = is.read(buf, 0, (int) Math.min(len, buf.length)); | ||||
if (n <= 0) | |||||
if (n <= 0) { | |||||
throw shortInput(len); | throw shortInput(len); | ||||
} | |||||
dOut.write(buf, 0, n); | dOut.write(buf, 0, n); | ||||
len -= n; | len -= n; | ||||
} | } | ||||
dOut.flush(); | dOut.flush(); | ||||
cOut.finish(); | cOut.finish(); | ||||
} finally { | } finally { | ||||
if (config.getFSyncObjectFiles()) | |||||
if (config.getFSyncObjectFiles()) { | |||||
fOut.getChannel().force(true); | fOut.getChannel().force(true); | ||||
fOut.close(); | |||||
} | |||||
} | } | ||||
delete = false; | delete = false; | ||||
return tmp; | return tmp; | ||||
} finally { | } finally { | ||||
if (delete) | |||||
if (delete) { | |||||
FileUtils.delete(tmp, FileUtils.RETRY); | FileUtils.delete(tmp, FileUtils.RETRY); | ||||
} | |||||
} | } | ||||
} | } | ||||
@SuppressWarnings("resource" /* java 7 */) | |||||
private File toTemp(final int type, final byte[] buf, final int pos, | private File toTemp(final int type, final byte[] buf, final int pos, | ||||
final int len) throws IOException, FileNotFoundException { | |||||
final int len) throws IOException { | |||||
boolean delete = true; | boolean delete = true; | ||||
File tmp = newTempFile(); | File tmp = newTempFile(); | ||||
try { | |||||
FileOutputStream fOut = new FileOutputStream(tmp); | |||||
try (FileOutputStream fOut = new FileOutputStream(tmp)) { | |||||
try { | try { | ||||
OutputStream out = fOut; | OutputStream out = fOut; | ||||
if (config.getFSyncObjectFiles()) | |||||
if (config.getFSyncObjectFiles()) { | |||||
out = Channels.newOutputStream(fOut.getChannel()); | out = Channels.newOutputStream(fOut.getChannel()); | ||||
} | |||||
DeflaterOutputStream cOut = compress(out); | DeflaterOutputStream cOut = compress(out); | ||||
writeHeader(cOut, type, len); | writeHeader(cOut, type, len); | ||||
cOut.write(buf, pos, len); | cOut.write(buf, pos, len); | ||||
cOut.finish(); | cOut.finish(); | ||||
} finally { | } finally { | ||||
if (config.getFSyncObjectFiles()) | |||||
if (config.getFSyncObjectFiles()) { | |||||
fOut.getChannel().force(true); | fOut.getChannel().force(true); | ||||
fOut.close(); | |||||
} | |||||
} | } | ||||
delete = false; | delete = false; | ||||
return tmp; | return tmp; | ||||
} finally { | } finally { | ||||
if (delete) | |||||
if (delete) { | |||||
FileUtils.delete(tmp, FileUtils.RETRY); | FileUtils.delete(tmp, FileUtils.RETRY); | ||||
} | |||||
} | } | ||||
} | } | ||||