asis.selectObjectRepresentation(pw, NullProgressMonitor.INSTANCE,
Collections.singleton(target));
asis.copyObjectAsIs(new PackOutputStream(NullProgressMonitor.INSTANCE,
- buf, pw), target);
+ buf, pw), target, true);
// At this point the object header has no delta information,
// because it was output as though it were a whole object.
}
final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
- WindowCursor curs) throws IOException,
+ boolean validate, WindowCursor curs) throws IOException,
StoredObjectRepresentationNotAvailableException {
beginCopyAsIs(src);
try {
- copyAsIs2(out, src, curs);
+ copyAsIs2(out, src, validate, curs);
} finally {
endCopyAsIs();
}
}
private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
- WindowCursor curs) throws IOException,
+ boolean validate, WindowCursor curs) throws IOException,
StoredObjectRepresentationNotAvailableException {
- final CRC32 crc1 = new CRC32();
- final CRC32 crc2 = new CRC32();
+ final CRC32 crc1 = validate ? new CRC32() : null;
+ final CRC32 crc2 = validate ? new CRC32() : null;
final byte[] buf = out.getCopyBuffer();
// Rip apart the header so we can discover the size.
do {
c = buf[headerCnt++] & 0xff;
} while ((c & 128) != 0);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
+ if (validate) {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
} else if (typeCode == Constants.OBJ_REF_DELTA) {
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
+ if (validate) {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
readFully(src.offset + headerCnt, buf, 0, 20, curs);
- crc1.update(buf, 0, 20);
- crc2.update(buf, 0, 20);
+ if (validate) {
+ crc1.update(buf, 0, 20);
+ crc2.update(buf, 0, 20);
+ }
headerCnt += 20;
- } else {
+ } else if (validate) {
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
try {
quickCopy = curs.quickCopy(this, dataOffset, dataLength);
- if (idx().hasCRC32Support()) {
+ if (validate && idx().hasCRC32Support()) {
// Index has the CRC32 code cached, validate the object.
//
expectedCRC = idx().findCRC32(src);
JGitText.get().objectAtHasBadZlibStream,
src.offset, getPackFile()));
}
- } else {
+ } else if (validate) {
// We don't have a CRC32 code in the index, so compute it
// now while inflating the raw data to get zlib to tell us
// whether or not the data is safe.
src.offset));
}
expectedCRC = crc1.getValue();
+ } else {
+ expectedCRC = -1;
}
} catch (DataFormatException dataFormat) {
setCorrupt(src.offset);
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, curs);
- crc2.update(buf, 0, n);
+ if (validate)
+ crc2.update(buf, 0, n);
out.write(buf, 0, n);
pos += n;
cnt -= n;
}
- if (crc2.getValue() != expectedCRC) {
+ if (validate && crc2.getValue() != expectedCRC) {
throw new CorruptObjectException(MessageFormat.format(JGitText
.get().objectAtHasBadZlibStream, src.offset,
getPackFile()));
}
}
- public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
- throws IOException, StoredObjectRepresentationNotAvailableException {
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
+ boolean validate) throws IOException,
+ StoredObjectRepresentationNotAvailableException {
LocalObjectToPack src = (LocalObjectToPack) otp;
- src.pack.copyAsIs(out, src, this);
+ src.pack.copyAsIs(out, src, validate, this);
}
public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
* reduce data locality for the reader, slowing down data access.
*
* Invoking {@link PackOutputStream#writeObject(ObjectToPack)} will cause
- * {@link #copyObjectAsIs(PackOutputStream, ObjectToPack)} to be invoked
- * recursively on {@code this} if the current object is scheduled for reuse.
+ * {@link #copyObjectAsIs(PackOutputStream, ObjectToPack, boolean)} to be
+ * invoked recursively on {@code this} if the current object is scheduled
+ * for reuse.
*
* @param out
* the stream to write each object to.
*
* <pre>
* MyToPack mtp = (MyToPack) otp;
- * byte[] raw = validate(mtp); // throw SORNAE here, if at all
+ * byte[] raw;
+ * if (validate)
+ * raw = validate(mtp); // throw SORNAE here, if at all
+ * else
+ * raw = readFast(mtp);
* out.writeHeader(mtp, mtp.inflatedSize);
* out.write(raw);
* </pre>
* stream the object should be written to.
* @param otp
* the object's saved representation information.
+ * @param validate
+ * if true the representation must be validated and not be
+ * corrupt before being reused. If false, validation may be
+ * skipped as it will be performed elsewhere in the processing
+ * pipeline.
* @throws StoredObjectRepresentationNotAvailableException
* the previously selected representation is no longer
* available. If thrown before {@code out.writeHeader} the pack
* the stream's write method threw an exception. Packing will
* abort.
*/
- public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
- throws IOException, StoredObjectRepresentationNotAvailableException;
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
+ boolean validate) throws IOException,
+ StoredObjectRepresentationNotAvailableException;
/**
* Obtain the available cached packs.
private boolean reuseDeltaCommits;
+ private boolean reuseValidate;
+
private boolean thin;
private boolean useCachedPacks;
deltaBaseAsOffset = config.isDeltaBaseAsOffset();
reuseDeltas = config.isReuseDeltas();
+ reuseValidate = true; // be paranoid by default
stats = new Statistics();
}
reuseDeltaCommits = reuse;
}
+ /**
+ * Check if the writer validates objects before copying them.
+ *
+ * @return true if validation is enabled; false if the reader will handle
+ * object validation as a side-effect of it consuming the output.
+ */
+ public boolean isReuseValidatingObjects() {
+ return reuseValidate;
+ }
+
+ /**
+ * Enable (or disable) object validation during packing.
+ *
+ * @param validate
+ * if true the pack writer will validate an object before it is
+ * put into the output. This additional validation work may be
+ * necessary to avoid propagating corruption from one local pack
+ * file to another local pack file.
+ */
+ public void setReuseValidatingObjects(boolean validate) {
+ reuseValidate = validate;
+ }
+
/** @return true if this writer is producing a thin pack. */
public boolean isThin() {
return thin;
while (otp.isReuseAsIs()) {
try {
- reuseSupport.copyObjectAsIs(out, otp);
+ reuseSupport.copyObjectAsIs(out, otp, reuseValidate);
out.endObject();
otp.setCRC(out.getCRC32());
stats.reusedObjects++;
writer.setUseCachedPacks(true);
writer.setThin(thinPack);
+ writer.setReuseValidatingObjects(false);
writer.setDeltaBaseAsOffset(capableOfsDelta);
writer.preparePack(monitor, newObjects, remoteObjects);
writer.writePack(monitor, monitor, out);
exc.add(r.getId());
packWriter.setDeltaBaseAsOffset(true);
packWriter.setThin(exc.size() > 0);
+ packWriter.setReuseValidatingObjects(false);
if (exc.size() == 0)
packWriter.setTagTargets(tagTargets);
packWriter.preparePack(monitor, inc, exc);
pw.setReuseDeltaCommits(true);
pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
pw.setThin(options.contains(OPTION_THIN_PACK));
+ pw.setReuseValidatingObjects(false);
if (commonBase.isEmpty()) {
Set<ObjectId> tagTargets = new HashSet<ObjectId>();