2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
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
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
;
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";
83 * Size of the internal stream buffer.
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;
94 * Create an index pack instance to load a new pack into a repository.
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.
102 * the repository that will receive the new pack.
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
)
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();
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());
124 private final Repository repo
;
126 private Inflater inflater
;
128 private final MessageDigest objectDigest
;
130 private final MutableObjectId tempObjectId
;
132 private InputStream in
;
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.
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.
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
{
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);
218 * Set the pack index file format version this instance will create.
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.
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.
237 * true to enable fixing a thin pack.
239 public void setFixThin(final boolean fix
) {
244 * Configure this index pack instance to keep an empty pack.
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
) {
257 * Configure the checker used to validate received objects.
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.
264 * the checker instance; null to disable object checking.
266 public void setObjectChecker(final ObjectChecker oc
) {
271 * Configure the checker used to validate received objects.
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.
277 * This is shorthand for:
280 * setObjectChecker(on ? new ObjectChecker() : null);
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.
296 * @throws IOException
298 public void index(final ProgressMonitor progress
) throws IOException
{
299 progress
.start(2 /* tasks */);
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
++) {
312 if (progress
.isCancelled())
313 throw new IOException("Download cancelled");
318 if (deltaCount
> 0) {
320 throw new IOException("need packOut");
321 resolveDeltas(progress
);
322 if (entryCount
< objectCount
) {
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);
338 if (dstIdx
!= null && (keepEmpty
|| entryCount
> 0))
343 InflaterCache
.release(inflater
);
347 readCurs
= WindowCursor
.release(readCurs
);
354 if (keepEmpty
|| entryCount
> 0) {
356 dstPack
.setReadOnly();
358 dstIdx
.setReadOnly();
360 } catch (IOException err
) {
369 private void resolveDeltas(final ProgressMonitor progress
)
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");
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
{
393 int c
= readFromFile();
394 final int typeCode
= (c
>> 4) & 7;
397 while ((c
& 0x80) != 0) {
399 sz
+= (c
& 0x7f) << shift
;
404 case Constants
.OBJ_COMMIT
:
405 case Constants
.OBJ_TREE
:
406 case Constants
.OBJ_BLOB
:
407 case Constants
.OBJ_TAG
:
409 data
= inflateFromFile((int) sz
);
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
));
418 case Constants
.OBJ_REF_DELTA
: {
419 crc
.update(buf
, fillFromFile(20), 20);
421 data
= BinaryDelta
.apply(data
, inflateFromFile((int) sz
));
425 throw new IOException("Unknown object type " + typeCode
+ ".");
428 final int crc32
= (int) crc
.getValue();
430 throw new IOException("Corruption detected re-reading at " + pos
);
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;
455 final UnresolvedDelta n
= c
.next
;
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);
472 resolveDeltas(b
.position
, b
.crc
, type
, data
, null);
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
{
483 resolveDeltas(a
.position
, a
.crc
, type
, data
, null);
488 private void fixThinPack(final ProgressMonitor progress
) throws IOException
{
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)
499 final ObjectLoader ldr
= repo
.openObject(readCurs
, baseId
);
504 final byte[] data
= ldr
.getCachedBytes();
505 final int typeCode
= ldr
.getType();
506 final PackedObjectInfo oe
;
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");
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
;
533 buf
[hdrlen
++] = (byte) ((typeCode
<< 4) | sz
& 15);
536 buf
[hdrlen
- 1] |= 0x80;
537 buf
[hdrlen
++] = (byte) (sz
& 0x7f);
540 packDigest
.update(buf
, 0, hdrlen
);
541 crc
.update(buf
, 0, hdrlen
);
542 packOut
.write(buf
, 0, hdrlen
);
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
)
556 final MessageDigest origDigest
= Constants
.newMessageDigest();
557 final MessageDigest tailDigest
= Constants
.newMessageDigest();
558 long origRemaining
= originalEOF
;
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
);
575 packOut
.write(buf
, 0, 12);
576 packOut
.seek(bAvail
);
579 packDigest
.update(buf
, 0, bAvail
);
581 final int n
= packOut
.read(buf
);
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
);
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
);
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
);
620 final PackIndexWriter iw
;
621 if (outputVersion
<= 0)
622 iw
= PackIndexWriter
.createOldestPossible(os
, list
);
624 iw
= PackIndexWriter
.createVersion(os
, outputVersion
);
625 iw
.write(list
, packcsum
);
626 os
.getChannel().force(true);
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);
646 private void readPackFooter() throws IOException
{
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);
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() {
666 // Read one entire object or delta from the input.
667 private void indexOneObject() throws IOException
{
668 final long pos
= position();
671 int c
= readFromInput();
672 final int typeCode
= (c
>> 4) & 7;
675 while ((c
& 0x80) != 0) {
677 sz
+= (c
& 0x7f) << shift
;
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
);
688 case Constants
.OBJ_OFS_DELTA
: {
691 while ((c
& 128) != 0) {
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
);
705 case Constants
.OBJ_REF_DELTA
: {
706 c
= fillFromInput(20);
707 crc
.update(buf
, c
, 20);
708 final ObjectId base
= ObjectId
.fromRaw(buf
, c
);
710 DeltaChain r
= baseById
.get(base
);
712 r
= new DeltaChain(base
);
715 skipInflateFromInput(sz
);
716 r
.add(new UnresolvedDelta(pos
, (int) crc
.getValue()));
721 throw new IOException("Unknown object type " + typeCode
+ ".");
725 private void whole(final int type
, final long pos
, final long sz
)
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) {
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
);
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
{
773 // Consume exactly one byte from the buffer and return it.
774 private int readFromInput() throws IOException
{
778 final int b
= buf
[bOffset
++] & 0xff;
783 // Consume exactly one byte from the buffer and return it.
784 private int readFromFile() throws IOException
{
788 final int b
= buf
[bOffset
++] & 0xff;
793 // Consume cnt bytes from the buffer.
794 private void use(final int 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
) {
807 free
= buf
.length
- next
;
809 next
= in
.read(buf
, next
, free
);
811 throw new EOFException("Packfile is truncated.");
817 // Ensure at least need bytes are available in in {@link #buf}.
818 private int fillFromFile(final int need
) throws IOException
{
820 int next
= bOffset
+ bAvail
;
821 int free
= buf
.length
- next
;
822 if (free
+ bAvail
< need
) {
824 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
827 free
= buf
.length
- next
;
829 next
= packOut
.read(buf
, next
, free
);
831 throw new EOFException("Packfile is truncated.");
837 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
838 private void sync() throws IOException
{
839 packDigest
.update(buf
, 0, bOffset
);
841 packOut
.write(buf
, 0, bOffset
);
843 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
848 private void skipInflateFromInput(long sz
) throws IOException
{
849 final Inflater inf
= inflater
;
851 final byte[] dst
= objectData
;
854 while (!inf
.finished()) {
855 if (inf
.needsInput()) {
857 crc
.update(buf
, p
, bAvail
);
860 p
= fillFromInput(1);
861 inf
.setInput(buf
, p
, bAvail
);
864 int free
= dst
.length
- n
;
870 n
+= inf
.inflate(dst
, n
, free
);
873 throw new DataFormatException("wrong decompressed length");
874 n
= bAvail
- inf
.getRemaining();
876 crc
.update(buf
, p
, n
);
879 } catch (DataFormatException dfe
) {
886 private byte[] inflateFromInput(final long sz
) throws IOException
{
887 final byte[] dst
= new byte[(int) sz
];
888 final Inflater inf
= inflater
;
892 while (!inf
.finished()) {
893 if (inf
.needsInput()) {
895 crc
.update(buf
, p
, bAvail
);
898 p
= fillFromInput(1);
899 inf
.setInput(buf
, p
, bAvail
);
902 n
+= inf
.inflate(dst
, n
, dst
.length
- n
);
905 throw new DataFormatException("wrong decompressed length");
906 n
= bAvail
- inf
.getRemaining();
908 crc
.update(buf
, p
, n
);
912 } catch (DataFormatException dfe
) {
919 private byte[] inflateFromFile(final int sz
) throws IOException
{
920 final Inflater inf
= inflater
;
922 final byte[] dst
= new byte[sz
];
925 while (!inf
.finished()) {
926 if (inf
.needsInput()) {
928 crc
.update(buf
, p
, bAvail
);
932 inf
.setInput(buf
, p
, bAvail
);
934 n
+= inf
.inflate(dst
, n
, sz
- n
);
936 n
= bAvail
- inf
.getRemaining();
938 crc
.update(buf
, p
, n
);
942 } catch (DataFormatException dfe
) {
949 private static CorruptObjectException
corrupt(final DataFormatException dfe
) {
950 return new CorruptObjectException("Packfile corruption detected: "
954 private static class DeltaChain
extends ObjectId
{
955 UnresolvedDelta head
;
957 DeltaChain(final AnyObjectId id
) {
961 UnresolvedDelta
remove() {
962 final UnresolvedDelta r
= head
;
968 void add(final UnresolvedDelta d
) {
974 private static class UnresolvedDelta
{
979 UnresolvedDelta next
;
981 UnresolvedDelta(final long headerOffset
, final int crc32
) {
982 position
= headerOffset
;
988 * Rename the pack to it's final name and location and open it.
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.
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();
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);
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();
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.
1052 if (!keep
.lock(lockMessage
))
1053 throw new IOException("Cannot lock pack in " + finalPack
);
1054 } catch (IOException e
) {
1055 cleanupTemporaryFiles();
1060 if (!dstPack
.renameTo(finalPack
)) {
1061 cleanupTemporaryFiles();
1063 throw new IOException("Cannot move pack to " + finalPack
);
1066 if (!dstIdx
.renameTo(finalIdx
)) {
1067 cleanupTemporaryFiles();
1069 if (!finalPack
.delete())
1070 finalPack
.deleteOnExit();
1071 throw new IOException("Cannot move index to " + finalIdx
);
1075 repo
.openPack(finalPack
, finalIdx
);
1076 } catch (IOException 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();