Use pack-*.keep files during fetch and receive-pack to prevent GC
[jgit.git] / org.spearce.jgit / src / org / spearce / jgit / transport / IndexPack.java
blob7881124667f60e339ce65edbf389b4a3014f38bf
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.transport;
41 import java.io.EOFException;
42 import java.io.File;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.RandomAccessFile;
47 import java.security.MessageDigest;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.zip.CRC32;
52 import java.util.zip.DataFormatException;
53 import java.util.zip.Deflater;
54 import java.util.zip.Inflater;
56 import org.spearce.jgit.errors.CorruptObjectException;
57 import org.spearce.jgit.errors.MissingObjectException;
58 import org.spearce.jgit.lib.AnyObjectId;
59 import org.spearce.jgit.lib.BinaryDelta;
60 import org.spearce.jgit.lib.Constants;
61 import org.spearce.jgit.lib.InflaterCache;
62 import org.spearce.jgit.lib.MutableObjectId;
63 import org.spearce.jgit.lib.ObjectChecker;
64 import org.spearce.jgit.lib.ObjectId;
65 import org.spearce.jgit.lib.ObjectIdSubclassMap;
66 import org.spearce.jgit.lib.ObjectLoader;
67 import org.spearce.jgit.lib.PackIndexWriter;
68 import org.spearce.jgit.lib.PackLock;
69 import org.spearce.jgit.lib.ProgressMonitor;
70 import org.spearce.jgit.lib.Repository;
71 import org.spearce.jgit.lib.WindowCursor;
72 import org.spearce.jgit.util.NB;
74 /** Indexes Git pack files for local use. */
75 public class IndexPack {
76 /** Progress message when reading raw data from the pack. */
77 public static final String PROGRESS_DOWNLOAD = "Receiving objects";
79 /** Progress message when computing names of delta compressed objects. */
80 public static final String PROGRESS_RESOLVE_DELTA = "Resolving deltas";
82 /**
83 * Size of the internal stream buffer.
84 * <p>
85 * If callers are going to be supplying IndexPack a BufferedInputStream they
86 * should use this buffer size as the size of the buffer for that
87 * BufferedInputStream, and any other its may be wrapping. This way the
88 * buffers will cascade efficiently and only the IndexPack buffer will be
89 * receiving the bulk of the data stream.
91 public static final int BUFFER_SIZE = 8192;
93 /**
94 * Create an index pack instance to load a new pack into a repository.
95 * <p>
96 * The received pack data and generated index will be saved to temporary
97 * files within the repository's <code>objects</code> directory. To use the
98 * data contained within them call {@link #renameAndOpenPack()} once the
99 * indexing is complete.
101 * @param db
102 * the repository that will receive the new pack.
103 * @param is
104 * stream to read the pack data from. If the stream is buffered
105 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
106 * @return a new index pack instance.
107 * @throws IOException
108 * a temporary file could not be created.
110 public static IndexPack create(final Repository db, final InputStream is)
111 throws IOException {
112 final String suffix = ".pack";
113 final File objdir = db.getObjectsDirectory();
114 final File tmp = File.createTempFile("incoming_", suffix, objdir);
115 final String n = tmp.getName();
116 final File base;
118 base = new File(objdir, n.substring(0, n.length() - suffix.length()));
119 final IndexPack ip = new IndexPack(db, is, base);
120 ip.setIndexVersion(db.getConfig().getCore().getPackIndexVersion());
121 return ip;
124 private final Repository repo;
126 private Inflater inflater;
128 private final MessageDigest objectDigest;
130 private final MutableObjectId tempObjectId;
132 private InputStream in;
134 private byte[] buf;
136 private long bBase;
138 private int bOffset;
140 private int bAvail;
142 private ObjectChecker objCheck;
144 private boolean fixThin;
146 private boolean keepEmpty;
148 private int outputVersion;
150 private final File dstPack;
152 private final File dstIdx;
154 private long objectCount;
156 private PackedObjectInfo[] entries;
158 private int deltaCount;
160 private int entryCount;
162 private final CRC32 crc = new CRC32();
164 private ObjectIdSubclassMap<DeltaChain> baseById;
166 private LongMap<UnresolvedDelta> baseByPos;
168 private byte[] objectData;
170 private MessageDigest packDigest;
172 private RandomAccessFile packOut;
174 private byte[] packcsum;
176 /** If {@link #fixThin} this is the last byte of the original checksum. */
177 private long originalEOF;
179 private WindowCursor readCurs;
182 * Create a new pack indexer utility.
184 * @param db
185 * @param src
186 * stream to read the pack data from. If the stream is buffered
187 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
188 * @param dstBase
189 * @throws IOException
190 * the output packfile could not be created.
192 public IndexPack(final Repository db, final InputStream src,
193 final File dstBase) throws IOException {
194 repo = db;
195 in = src;
196 inflater = InflaterCache.get();
197 readCurs = new WindowCursor();
198 buf = new byte[BUFFER_SIZE];
199 objectData = new byte[BUFFER_SIZE];
200 objectDigest = Constants.newMessageDigest();
201 tempObjectId = new MutableObjectId();
202 packDigest = Constants.newMessageDigest();
204 if (dstBase != null) {
205 final File dir = dstBase.getParentFile();
206 final String nam = dstBase.getName();
207 dstPack = new File(dir, nam + ".pack");
208 dstIdx = new File(dir, nam + ".idx");
209 packOut = new RandomAccessFile(dstPack, "rw");
210 packOut.setLength(0);
211 } else {
212 dstPack = null;
213 dstIdx = null;
218 * Set the pack index file format version this instance will create.
220 * @param version
221 * the version to write. The special version 0 designates the
222 * oldest (most compatible) format available for the objects.
223 * @see PackIndexWriter
225 public void setIndexVersion(final int version) {
226 outputVersion = version;
230 * Configure this index pack instance to make a thin pack complete.
231 * <p>
232 * Thin packs are sometimes used during network transfers to allow a delta
233 * to be sent without a base object. Such packs are not permitted on disk.
234 * They can be fixed by copying the base object onto the end of the pack.
236 * @param fix
237 * true to enable fixing a thin pack.
239 public void setFixThin(final boolean fix) {
240 fixThin = fix;
244 * Configure this index pack instance to keep an empty pack.
245 * <p>
246 * By default an empty pack (a pack with no objects) is not kept, as doing
247 * so is completely pointless. With no objects in the pack there is no data
248 * stored by it, so the pack is unnecessary.
250 * @param empty true to enable keeping an empty pack.
252 public void setKeepEmpty(final boolean empty) {
253 keepEmpty = empty;
257 * Configure the checker used to validate received objects.
258 * <p>
259 * Usually object checking isn't necessary, as Git implementations only
260 * create valid objects in pack files. However, additional checking may be
261 * useful if processing data from an untrusted source.
263 * @param oc
264 * the checker instance; null to disable object checking.
266 public void setObjectChecker(final ObjectChecker oc) {
267 objCheck = oc;
271 * Configure the checker used to validate received objects.
272 * <p>
273 * Usually object checking isn't necessary, as Git implementations only
274 * create valid objects in pack files. However, additional checking may be
275 * useful if processing data from an untrusted source.
276 * <p>
277 * This is shorthand for:
279 * <pre>
280 * setObjectChecker(on ? new ObjectChecker() : null);
281 * </pre>
283 * @param on
284 * true to enable the default checker; false to disable it.
286 public void setObjectChecking(final boolean on) {
287 setObjectChecker(on ? new ObjectChecker() : null);
291 * Consume data from the input stream until the packfile is indexed.
293 * @param progress
294 * progress feedback
296 * @throws IOException
298 public void index(final ProgressMonitor progress) throws IOException {
299 progress.start(2 /* tasks */);
300 try {
301 try {
302 readPackHeader();
304 entries = new PackedObjectInfo[(int) objectCount];
305 baseById = new ObjectIdSubclassMap<DeltaChain>();
306 baseByPos = new LongMap<UnresolvedDelta>();
308 progress.beginTask(PROGRESS_DOWNLOAD, (int) objectCount);
309 for (int done = 0; done < objectCount; done++) {
310 indexOneObject();
311 progress.update(1);
312 if (progress.isCancelled())
313 throw new IOException("Download cancelled");
315 readPackFooter();
316 endInput();
317 progress.endTask();
318 if (deltaCount > 0) {
319 if (packOut == null)
320 throw new IOException("need packOut");
321 resolveDeltas(progress);
322 if (entryCount < objectCount) {
323 if (!fixThin) {
324 throw new IOException("pack has "
325 + (objectCount - entryCount)
326 + " unresolved deltas");
328 fixThinPack(progress);
331 if (packOut != null && (keepEmpty || entryCount > 0))
332 packOut.getChannel().force(true);
334 packDigest = null;
335 baseById = null;
336 baseByPos = null;
338 if (dstIdx != null && (keepEmpty || entryCount > 0))
339 writeIdx();
341 } finally {
342 try {
343 InflaterCache.release(inflater);
344 } finally {
345 inflater = null;
347 readCurs = WindowCursor.release(readCurs);
349 progress.endTask();
350 if (packOut != null)
351 packOut.close();
354 if (keepEmpty || entryCount > 0) {
355 if (dstPack != null)
356 dstPack.setReadOnly();
357 if (dstIdx != null)
358 dstIdx.setReadOnly();
360 } catch (IOException err) {
361 if (dstPack != null)
362 dstPack.delete();
363 if (dstIdx != null)
364 dstIdx.delete();
365 throw err;
369 private void resolveDeltas(final ProgressMonitor progress)
370 throws IOException {
371 progress.beginTask(PROGRESS_RESOLVE_DELTA, deltaCount);
372 final int last = entryCount;
373 for (int i = 0; i < last; i++) {
374 final int before = entryCount;
375 resolveDeltas(entries[i]);
376 progress.update(entryCount - before);
377 if (progress.isCancelled())
378 throw new IOException("Download cancelled during indexing");
380 progress.endTask();
383 private void resolveDeltas(final PackedObjectInfo oe) throws IOException {
384 final int oldCRC = oe.getCRC();
385 if (baseById.get(oe) != null || baseByPos.containsKey(oe.getOffset()))
386 resolveDeltas(oe.getOffset(), oldCRC, Constants.OBJ_BAD, null, oe);
389 private void resolveDeltas(final long pos, final int oldCRC, int type,
390 byte[] data, PackedObjectInfo oe) throws IOException {
391 crc.reset();
392 position(pos);
393 int c = readFromFile();
394 final int typeCode = (c >> 4) & 7;
395 long sz = c & 15;
396 int shift = 4;
397 while ((c & 0x80) != 0) {
398 c = readFromFile();
399 sz += (c & 0x7f) << shift;
400 shift += 7;
403 switch (typeCode) {
404 case Constants.OBJ_COMMIT:
405 case Constants.OBJ_TREE:
406 case Constants.OBJ_BLOB:
407 case Constants.OBJ_TAG:
408 type = typeCode;
409 data = inflateFromFile((int) sz);
410 break;
411 case Constants.OBJ_OFS_DELTA: {
412 c = readFromFile() & 0xff;
413 while ((c & 128) != 0)
414 c = readFromFile() & 0xff;
415 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
416 break;
418 case Constants.OBJ_REF_DELTA: {
419 crc.update(buf, fillFromFile(20), 20);
420 use(20);
421 data = BinaryDelta.apply(data, inflateFromFile((int) sz));
422 break;
424 default:
425 throw new IOException("Unknown object type " + typeCode + ".");
428 final int crc32 = (int) crc.getValue();
429 if (oldCRC != crc32)
430 throw new IOException("Corruption detected re-reading at " + pos);
431 if (oe == null) {
432 objectDigest.update(Constants.encodedTypeString(type));
433 objectDigest.update((byte) ' ');
434 objectDigest.update(Constants.encodeASCII(data.length));
435 objectDigest.update((byte) 0);
436 objectDigest.update(data);
437 tempObjectId.fromRaw(objectDigest.digest(), 0);
439 verifySafeObject(tempObjectId, type, data);
440 oe = new PackedObjectInfo(pos, crc32, tempObjectId);
441 entries[entryCount++] = oe;
444 resolveChildDeltas(pos, type, data, oe);
447 private UnresolvedDelta removeBaseById(final AnyObjectId id){
448 final DeltaChain d = baseById.get(id);
449 return d != null ? d.remove() : null;
452 private static UnresolvedDelta reverse(UnresolvedDelta c) {
453 UnresolvedDelta tail = null;
454 while (c != null) {
455 final UnresolvedDelta n = c.next;
456 c.next = tail;
457 tail = c;
458 c = n;
460 return tail;
463 private void resolveChildDeltas(final long pos, int type, byte[] data,
464 PackedObjectInfo oe) throws IOException {
465 UnresolvedDelta a = reverse(removeBaseById(oe));
466 UnresolvedDelta b = reverse(baseByPos.remove(pos));
467 while (a != null && b != null) {
468 if (a.position < b.position) {
469 resolveDeltas(a.position, a.crc, type, data, null);
470 a = a.next;
471 } else {
472 resolveDeltas(b.position, b.crc, type, data, null);
473 b = b.next;
476 resolveChildDeltaChain(type, data, a);
477 resolveChildDeltaChain(type, data, b);
480 private void resolveChildDeltaChain(final int type, final byte[] data,
481 UnresolvedDelta a) throws IOException {
482 while (a != null) {
483 resolveDeltas(a.position, a.crc, type, data, null);
484 a = a.next;
488 private void fixThinPack(final ProgressMonitor progress) throws IOException {
489 growEntries();
491 packDigest.reset();
492 originalEOF = packOut.length() - 20;
493 final Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
494 final List<DeltaChain> missing = new ArrayList<DeltaChain>(64);
495 long end = originalEOF;
496 for (final DeltaChain baseId : baseById) {
497 if (baseId.head == null)
498 continue;
499 final ObjectLoader ldr = repo.openObject(readCurs, baseId);
500 if (ldr == null) {
501 missing.add(baseId);
502 continue;
504 final byte[] data = ldr.getCachedBytes();
505 final int typeCode = ldr.getType();
506 final PackedObjectInfo oe;
508 crc.reset();
509 packOut.seek(end);
510 writeWhole(def, typeCode, data);
511 oe = new PackedObjectInfo(end, (int) crc.getValue(), baseId);
512 entries[entryCount++] = oe;
513 end = packOut.getFilePointer();
515 resolveChildDeltas(oe.getOffset(), typeCode, data, oe);
516 if (progress.isCancelled())
517 throw new IOException("Download cancelled during indexing");
519 def.end();
521 for (final DeltaChain base : missing) {
522 if (base.head != null)
523 throw new MissingObjectException(base, "delta base");
526 fixHeaderFooter(packcsum, packDigest.digest());
529 private void writeWhole(final Deflater def, final int typeCode,
530 final byte[] data) throws IOException {
531 int sz = data.length;
532 int hdrlen = 0;
533 buf[hdrlen++] = (byte) ((typeCode << 4) | sz & 15);
534 sz >>>= 4;
535 while (sz > 0) {
536 buf[hdrlen - 1] |= 0x80;
537 buf[hdrlen++] = (byte) (sz & 0x7f);
538 sz >>>= 7;
540 packDigest.update(buf, 0, hdrlen);
541 crc.update(buf, 0, hdrlen);
542 packOut.write(buf, 0, hdrlen);
543 def.reset();
544 def.setInput(data);
545 def.finish();
546 while (!def.finished()) {
547 final int datlen = def.deflate(buf);
548 packDigest.update(buf, 0, datlen);
549 crc.update(buf, 0, datlen);
550 packOut.write(buf, 0, datlen);
554 private void fixHeaderFooter(final byte[] origcsum, final byte[] tailcsum)
555 throws IOException {
556 final MessageDigest origDigest = Constants.newMessageDigest();
557 final MessageDigest tailDigest = Constants.newMessageDigest();
558 long origRemaining = originalEOF;
560 packOut.seek(0);
561 bAvail = 0;
562 bOffset = 0;
563 fillFromFile(12);
566 final int origCnt = (int) Math.min(bAvail, origRemaining);
567 origDigest.update(buf, 0, origCnt);
568 origRemaining -= origCnt;
569 if (origRemaining == 0)
570 tailDigest.update(buf, origCnt, bAvail - origCnt);
573 NB.encodeInt32(buf, 8, entryCount);
574 packOut.seek(0);
575 packOut.write(buf, 0, 12);
576 packOut.seek(bAvail);
578 packDigest.reset();
579 packDigest.update(buf, 0, bAvail);
580 for (;;) {
581 final int n = packOut.read(buf);
582 if (n < 0)
583 break;
584 if (origRemaining != 0) {
585 final int origCnt = (int) Math.min(n, origRemaining);
586 origDigest.update(buf, 0, origCnt);
587 origRemaining -= origCnt;
588 if (origRemaining == 0)
589 tailDigest.update(buf, origCnt, n - origCnt);
590 } else
591 tailDigest.update(buf, 0, n);
593 packDigest.update(buf, 0, n);
596 if (!Arrays.equals(origDigest.digest(), origcsum)
597 || !Arrays.equals(tailDigest.digest(), tailcsum))
598 throw new IOException("Pack corrupted while writing to filesystem");
600 packcsum = packDigest.digest();
601 packOut.write(packcsum);
604 private void growEntries() {
605 final PackedObjectInfo[] ne;
607 ne = new PackedObjectInfo[(int) objectCount + baseById.size()];
608 System.arraycopy(entries, 0, ne, 0, entryCount);
609 entries = ne;
612 private void writeIdx() throws IOException {
613 Arrays.sort(entries, 0, entryCount);
614 List<PackedObjectInfo> list = Arrays.asList(entries);
615 if (entryCount < entries.length)
616 list = list.subList(0, entryCount);
618 final FileOutputStream os = new FileOutputStream(dstIdx);
619 try {
620 final PackIndexWriter iw;
621 if (outputVersion <= 0)
622 iw = PackIndexWriter.createOldestPossible(os, list);
623 else
624 iw = PackIndexWriter.createVersion(os, outputVersion);
625 iw.write(list, packcsum);
626 os.getChannel().force(true);
627 } finally {
628 os.close();
632 private void readPackHeader() throws IOException {
633 final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
634 final int p = fillFromInput(hdrln);
635 for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
636 if (buf[p + k] != Constants.PACK_SIGNATURE[k])
637 throw new IOException("Not a PACK file.");
639 final long vers = NB.decodeUInt32(buf, p + 4);
640 if (vers != 2 && vers != 3)
641 throw new IOException("Unsupported pack version " + vers + ".");
642 objectCount = NB.decodeUInt32(buf, p + 8);
643 use(hdrln);
646 private void readPackFooter() throws IOException {
647 sync();
648 final byte[] cmpcsum = packDigest.digest();
649 final int c = fillFromInput(20);
650 packcsum = new byte[20];
651 System.arraycopy(buf, c, packcsum, 0, 20);
652 use(20);
653 if (packOut != null)
654 packOut.write(packcsum);
656 if (!Arrays.equals(cmpcsum, packcsum))
657 throw new CorruptObjectException("Packfile checksum incorrect.");
660 // Cleanup all resources associated with our input parsing.
661 private void endInput() {
662 in = null;
663 objectData = null;
666 // Read one entire object or delta from the input.
667 private void indexOneObject() throws IOException {
668 final long pos = position();
670 crc.reset();
671 int c = readFromInput();
672 final int typeCode = (c >> 4) & 7;
673 long sz = c & 15;
674 int shift = 4;
675 while ((c & 0x80) != 0) {
676 c = readFromInput();
677 sz += (c & 0x7f) << shift;
678 shift += 7;
681 switch (typeCode) {
682 case Constants.OBJ_COMMIT:
683 case Constants.OBJ_TREE:
684 case Constants.OBJ_BLOB:
685 case Constants.OBJ_TAG:
686 whole(typeCode, pos, sz);
687 break;
688 case Constants.OBJ_OFS_DELTA: {
689 c = readFromInput();
690 long ofs = c & 127;
691 while ((c & 128) != 0) {
692 ofs += 1;
693 c = readFromInput();
694 ofs <<= 7;
695 ofs += (c & 127);
697 final long base = pos - ofs;
698 final UnresolvedDelta n;
699 skipInflateFromInput(sz);
700 n = new UnresolvedDelta(pos, (int) crc.getValue());
701 n.next = baseByPos.put(base, n);
702 deltaCount++;
703 break;
705 case Constants.OBJ_REF_DELTA: {
706 c = fillFromInput(20);
707 crc.update(buf, c, 20);
708 final ObjectId base = ObjectId.fromRaw(buf, c);
709 use(20);
710 DeltaChain r = baseById.get(base);
711 if (r == null) {
712 r = new DeltaChain(base);
713 baseById.add(r);
715 skipInflateFromInput(sz);
716 r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
717 deltaCount++;
718 break;
720 default:
721 throw new IOException("Unknown object type " + typeCode + ".");
725 private void whole(final int type, final long pos, final long sz)
726 throws IOException {
727 final byte[] data = inflateFromInput(sz);
728 objectDigest.update(Constants.encodedTypeString(type));
729 objectDigest.update((byte) ' ');
730 objectDigest.update(Constants.encodeASCII(sz));
731 objectDigest.update((byte) 0);
732 objectDigest.update(data);
733 tempObjectId.fromRaw(objectDigest.digest(), 0);
735 verifySafeObject(tempObjectId, type, data);
736 final int crc32 = (int) crc.getValue();
737 entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId);
740 private void verifySafeObject(final AnyObjectId id, final int type,
741 final byte[] data) throws IOException {
742 if (objCheck != null) {
743 try {
744 objCheck.check(type, data);
745 } catch (CorruptObjectException e) {
746 throw new IOException("Invalid "
747 + Constants.encodedTypeString(type) + " " + id.name()
748 + ":" + e.getMessage());
752 final ObjectLoader ldr = repo.openObject(readCurs, id);
753 if (ldr != null) {
754 final byte[] existingData = ldr.getCachedBytes();
755 if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
756 throw new IOException("Collision on " + id.name());
761 // Current position of {@link #bOffset} within the entire file.
762 private long position() {
763 return bBase + bOffset;
766 private void position(final long pos) throws IOException {
767 packOut.seek(pos);
768 bBase = pos;
769 bOffset = 0;
770 bAvail = 0;
773 // Consume exactly one byte from the buffer and return it.
774 private int readFromInput() throws IOException {
775 if (bAvail == 0)
776 fillFromInput(1);
777 bAvail--;
778 final int b = buf[bOffset++] & 0xff;
779 crc.update(b);
780 return b;
783 // Consume exactly one byte from the buffer and return it.
784 private int readFromFile() throws IOException {
785 if (bAvail == 0)
786 fillFromFile(1);
787 bAvail--;
788 final int b = buf[bOffset++] & 0xff;
789 crc.update(b);
790 return b;
793 // Consume cnt bytes from the buffer.
794 private void use(final int cnt) {
795 bOffset += cnt;
796 bAvail -= cnt;
799 // Ensure at least need bytes are available in in {@link #buf}.
800 private int fillFromInput(final int need) throws IOException {
801 while (bAvail < need) {
802 int next = bOffset + bAvail;
803 int free = buf.length - next;
804 if (free + bAvail < need) {
805 sync();
806 next = bAvail;
807 free = buf.length - next;
809 next = in.read(buf, next, free);
810 if (next <= 0)
811 throw new EOFException("Packfile is truncated.");
812 bAvail += next;
814 return bOffset;
817 // Ensure at least need bytes are available in in {@link #buf}.
818 private int fillFromFile(final int need) throws IOException {
819 if (bAvail < need) {
820 int next = bOffset + bAvail;
821 int free = buf.length - next;
822 if (free + bAvail < need) {
823 if (bAvail > 0)
824 System.arraycopy(buf, bOffset, buf, 0, bAvail);
825 bOffset = 0;
826 next = bAvail;
827 free = buf.length - next;
829 next = packOut.read(buf, next, free);
830 if (next <= 0)
831 throw new EOFException("Packfile is truncated.");
832 bAvail += next;
834 return bOffset;
837 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
838 private void sync() throws IOException {
839 packDigest.update(buf, 0, bOffset);
840 if (packOut != null)
841 packOut.write(buf, 0, bOffset);
842 if (bAvail > 0)
843 System.arraycopy(buf, bOffset, buf, 0, bAvail);
844 bBase += bOffset;
845 bOffset = 0;
848 private void skipInflateFromInput(long sz) throws IOException {
849 final Inflater inf = inflater;
850 try {
851 final byte[] dst = objectData;
852 int n = 0;
853 int p = -1;
854 while (!inf.finished()) {
855 if (inf.needsInput()) {
856 if (p >= 0) {
857 crc.update(buf, p, bAvail);
858 use(bAvail);
860 p = fillFromInput(1);
861 inf.setInput(buf, p, bAvail);
864 int free = dst.length - n;
865 if (free < 8) {
866 sz -= n;
867 n = 0;
868 free = dst.length;
870 n += inf.inflate(dst, n, free);
872 if (n != sz)
873 throw new DataFormatException("wrong decompressed length");
874 n = bAvail - inf.getRemaining();
875 if (n > 0) {
876 crc.update(buf, p, n);
877 use(n);
879 } catch (DataFormatException dfe) {
880 throw corrupt(dfe);
881 } finally {
882 inf.reset();
886 private byte[] inflateFromInput(final long sz) throws IOException {
887 final byte[] dst = new byte[(int) sz];
888 final Inflater inf = inflater;
889 try {
890 int n = 0;
891 int p = -1;
892 while (!inf.finished()) {
893 if (inf.needsInput()) {
894 if (p >= 0) {
895 crc.update(buf, p, bAvail);
896 use(bAvail);
898 p = fillFromInput(1);
899 inf.setInput(buf, p, bAvail);
902 n += inf.inflate(dst, n, dst.length - n);
904 if (n != sz)
905 throw new DataFormatException("wrong decompressed length");
906 n = bAvail - inf.getRemaining();
907 if (n > 0) {
908 crc.update(buf, p, n);
909 use(n);
911 return dst;
912 } catch (DataFormatException dfe) {
913 throw corrupt(dfe);
914 } finally {
915 inf.reset();
919 private byte[] inflateFromFile(final int sz) throws IOException {
920 final Inflater inf = inflater;
921 try {
922 final byte[] dst = new byte[sz];
923 int n = 0;
924 int p = -1;
925 while (!inf.finished()) {
926 if (inf.needsInput()) {
927 if (p >= 0) {
928 crc.update(buf, p, bAvail);
929 use(bAvail);
931 p = fillFromFile(1);
932 inf.setInput(buf, p, bAvail);
934 n += inf.inflate(dst, n, sz - n);
936 n = bAvail - inf.getRemaining();
937 if (n > 0) {
938 crc.update(buf, p, n);
939 use(n);
941 return dst;
942 } catch (DataFormatException dfe) {
943 throw corrupt(dfe);
944 } finally {
945 inf.reset();
949 private static CorruptObjectException corrupt(final DataFormatException dfe) {
950 return new CorruptObjectException("Packfile corruption detected: "
951 + dfe.getMessage());
954 private static class DeltaChain extends ObjectId {
955 UnresolvedDelta head;
957 DeltaChain(final AnyObjectId id) {
958 super(id);
961 UnresolvedDelta remove() {
962 final UnresolvedDelta r = head;
963 if (r != null)
964 head = null;
965 return r;
968 void add(final UnresolvedDelta d) {
969 d.next = head;
970 head = d;
974 private static class UnresolvedDelta {
975 final long position;
977 final int crc;
979 UnresolvedDelta next;
981 UnresolvedDelta(final long headerOffset, final int crc32) {
982 position = headerOffset;
983 crc = crc32;
988 * Rename the pack to it's final name and location and open it.
989 * <p>
990 * If the call completes successfully the repository this IndexPack instance
991 * was created with will have the objects in the pack available for reading
992 * and use, without needing to scan for packs.
994 * @throws IOException
995 * The pack could not be inserted into the repository's objects
996 * directory. The pack no longer exists on disk, as it was
997 * removed prior to throwing the exception to the caller.
999 public void renameAndOpenPack() throws IOException {
1000 renameAndOpenPack(null);
1004 * Rename the pack to it's final name and location and open it.
1005 * <p>
1006 * If the call completes successfully the repository this IndexPack instance
1007 * was created with will have the objects in the pack available for reading
1008 * and use, without needing to scan for packs.
1010 * @param lockMessage
1011 * message to place in the pack-*.keep file. If null, no lock
1012 * will be created, and this method returns null.
1013 * @return the pack lock object, if lockMessage is not null.
1014 * @throws IOException
1015 * The pack could not be inserted into the repository's objects
1016 * directory. The pack no longer exists on disk, as it was
1017 * removed prior to throwing the exception to the caller.
1019 public PackLock renameAndOpenPack(final String lockMessage)
1020 throws IOException {
1021 if (!keepEmpty && entryCount == 0) {
1022 cleanupTemporaryFiles();
1023 return null;
1026 final MessageDigest d = Constants.newMessageDigest();
1027 final byte[] oeBytes = new byte[Constants.OBJECT_ID_LENGTH];
1028 for (int i = 0; i < entryCount; i++) {
1029 final PackedObjectInfo oe = entries[i];
1030 oe.copyRawTo(oeBytes, 0);
1031 d.update(oeBytes);
1034 final String name = ObjectId.fromRaw(d.digest()).name();
1035 final File packDir = new File(repo.getObjectsDirectory(), "pack");
1036 final File finalPack = new File(packDir, "pack-" + name + ".pack");
1037 final File finalIdx = new File(packDir, "pack-" + name + ".idx");
1038 final PackLock keep = new PackLock(finalPack);
1040 if (finalPack.exists()) {
1041 // If the pack is already present we should never replace it.
1043 cleanupTemporaryFiles();
1044 return null;
1047 if (lockMessage != null) {
1048 // If we have a reason to create a keep file for this pack, do
1049 // so, or fail fast and don't put the pack in place.
1051 try {
1052 if (!keep.lock(lockMessage))
1053 throw new IOException("Cannot lock pack in " + finalPack);
1054 } catch (IOException e) {
1055 cleanupTemporaryFiles();
1056 throw e;
1060 if (!dstPack.renameTo(finalPack)) {
1061 cleanupTemporaryFiles();
1062 keep.unlock();
1063 throw new IOException("Cannot move pack to " + finalPack);
1066 if (!dstIdx.renameTo(finalIdx)) {
1067 cleanupTemporaryFiles();
1068 keep.unlock();
1069 if (!finalPack.delete())
1070 finalPack.deleteOnExit();
1071 throw new IOException("Cannot move index to " + finalIdx);
1074 try {
1075 repo.openPack(finalPack, finalIdx);
1076 } catch (IOException err) {
1077 keep.unlock();
1078 finalPack.delete();
1079 finalIdx.delete();
1080 throw err;
1083 return lockMessage != null ? keep : null;
1086 private void cleanupTemporaryFiles() {
1087 if (!dstIdx.delete())
1088 dstIdx.deleteOnExit();
1089 if (!dstPack.delete())
1090 dstPack.deleteOnExit();