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
.HashMap
;
51 import java
.util
.List
;
52 import java
.util
.zip
.CRC32
;
53 import java
.util
.zip
.DataFormatException
;
54 import java
.util
.zip
.Deflater
;
55 import java
.util
.zip
.Inflater
;
57 import org
.spearce
.jgit
.errors
.CorruptObjectException
;
58 import org
.spearce
.jgit
.errors
.MissingObjectException
;
59 import org
.spearce
.jgit
.lib
.AnyObjectId
;
60 import org
.spearce
.jgit
.lib
.BinaryDelta
;
61 import org
.spearce
.jgit
.lib
.Constants
;
62 import org
.spearce
.jgit
.lib
.InflaterCache
;
63 import org
.spearce
.jgit
.lib
.MutableObjectId
;
64 import org
.spearce
.jgit
.lib
.ObjectChecker
;
65 import org
.spearce
.jgit
.lib
.ObjectId
;
66 import org
.spearce
.jgit
.lib
.ObjectIdMap
;
67 import org
.spearce
.jgit
.lib
.ObjectLoader
;
68 import org
.spearce
.jgit
.lib
.PackIndexWriter
;
69 import org
.spearce
.jgit
.lib
.ProgressMonitor
;
70 import org
.spearce
.jgit
.lib
.Repository
;
71 import org
.spearce
.jgit
.util
.NB
;
73 /** Indexes Git pack files for local use. */
74 public class IndexPack
{
75 /** Progress message when reading raw data from the pack. */
76 public static final String PROGRESS_DOWNLOAD
= "Receiving objects";
78 /** Progress message when computing names of delta compressed objects. */
79 public static final String PROGRESS_RESOLVE_DELTA
= "Resolving deltas";
82 * Size of the internal stream buffer.
84 * If callers are going to be supplying IndexPack a BufferedInputStream they
85 * should use this buffer size as the size of the buffer for that
86 * BufferedInputStream, and any other its may be wrapping. This way the
87 * buffers will cascade efficiently and only the IndexPack buffer will be
88 * receiving the bulk of the data stream.
90 public static final int BUFFER_SIZE
= 8192;
93 * Create an index pack instance to load a new pack into a repository.
95 * The received pack data and generated index will be saved to temporary
96 * files within the repository's <code>objects</code> directory. To use the
97 * data contained within them call {@link #renameAndOpenPack()} once the
98 * indexing is complete.
101 * the repository that will receive the new pack.
103 * stream to read the pack data from. If the stream is buffered
104 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
105 * @return a new index pack instance.
106 * @throws IOException
107 * a temporary file could not be created.
109 public static IndexPack
create(final Repository db
, final InputStream is
)
111 final String suffix
= ".pack";
112 final File objdir
= db
.getObjectsDirectory();
113 final File tmp
= File
.createTempFile("incoming_", suffix
, objdir
);
114 final String n
= tmp
.getName();
117 base
= new File(objdir
, n
.substring(0, n
.length() - suffix
.length()));
118 final IndexPack ip
= new IndexPack(db
, is
, base
);
119 ip
.setIndexVersion(db
.getConfig().getCore().getPackIndexVersion());
123 private final Repository repo
;
125 private Inflater inflater
;
127 private final MessageDigest objectDigest
;
129 private final MutableObjectId tempObjectId
;
131 private InputStream in
;
141 private ObjectChecker objCheck
;
143 private boolean fixThin
;
145 private int outputVersion
;
147 private final File dstPack
;
149 private final File dstIdx
;
151 private long objectCount
;
153 private PackedObjectInfo
[] entries
;
155 private int deltaCount
;
157 private int entryCount
;
159 private final CRC32 crc
= new CRC32();
161 private ObjectIdMap
<ArrayList
<UnresolvedDelta
>> baseById
;
163 private HashMap
<Long
, ArrayList
<UnresolvedDelta
>> baseByPos
;
165 private byte[] objectData
;
167 private MessageDigest packDigest
;
169 private RandomAccessFile packOut
;
171 private byte[] packcsum
;
173 /** If {@link #fixThin} this is the last byte of the original checksum. */
174 private long originalEOF
;
177 * Create a new pack indexer utility.
181 * stream to read the pack data from. If the stream is buffered
182 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
184 * @throws IOException
185 * the output packfile could not be created.
187 public IndexPack(final Repository db
, final InputStream src
,
188 final File dstBase
) throws IOException
{
191 inflater
= InflaterCache
.get();
192 buf
= new byte[BUFFER_SIZE
];
193 objectData
= new byte[BUFFER_SIZE
];
194 objectDigest
= Constants
.newMessageDigest();
195 tempObjectId
= new MutableObjectId();
196 packDigest
= Constants
.newMessageDigest();
198 if (dstBase
!= null) {
199 final File dir
= dstBase
.getParentFile();
200 final String nam
= dstBase
.getName();
201 dstPack
= new File(dir
, nam
+ ".pack");
202 dstIdx
= new File(dir
, nam
+ ".idx");
203 packOut
= new RandomAccessFile(dstPack
, "rw");
204 packOut
.setLength(0);
212 * Set the pack index file format version this instance will create.
215 * the version to write. The special version 0 designates the
216 * oldest (most compatible) format available for the objects.
217 * @see PackIndexWriter
219 public void setIndexVersion(final int version
) {
220 outputVersion
= version
;
224 * Configure this index pack instance to make a thin pack complete.
226 * Thin packs are sometimes used during network transfers to allow a delta
227 * to be sent without a base object. Such packs are not permitted on disk.
228 * They can be fixed by copying the base object onto the end of the pack.
231 * true to enable fixing a thin pack.
233 public void setFixThin(final boolean fix
) {
238 * Configure the checker used to validate received objects.
240 * Usually object checking isn't necessary, as Git implementations only
241 * create valid objects in pack files. However, additional checking may be
242 * useful if processing data from an untrusted source.
245 * the checker instance; null to disable object checking.
247 public void setObjectChecker(final ObjectChecker oc
) {
252 * Configure the checker used to validate received objects.
254 * Usually object checking isn't necessary, as Git implementations only
255 * create valid objects in pack files. However, additional checking may be
256 * useful if processing data from an untrusted source.
258 * This is shorthand for:
261 * setObjectChecker(on ? new ObjectChecker() : null);
265 * true to enable the default checker; false to disable it.
267 public void setObjectChecking(final boolean on
) {
268 setObjectChecker(on ?
new ObjectChecker() : null);
272 * Consume data from the input stream until the packfile is indexed.
277 * @throws IOException
279 public void index(final ProgressMonitor progress
) throws IOException
{
280 progress
.start(2 /* tasks */);
285 entries
= new PackedObjectInfo
[(int) objectCount
];
286 baseById
= new ObjectIdMap
<ArrayList
<UnresolvedDelta
>>();
287 baseByPos
= new HashMap
<Long
, ArrayList
<UnresolvedDelta
>>();
289 progress
.beginTask(PROGRESS_DOWNLOAD
, (int) objectCount
);
290 for (int done
= 0; done
< objectCount
; done
++) {
293 if (progress
.isCancelled())
294 throw new IOException("Download cancelled");
299 if (deltaCount
> 0) {
301 throw new IOException("need packOut");
302 resolveDeltas(progress
);
303 if (entryCount
< objectCount
) {
305 throw new IOException("pack has "
306 + (objectCount
- entryCount
)
307 + " unresolved deltas");
309 fixThinPack(progress
);
313 packOut
.getChannel().force(true);
323 final Inflater inf
= inflater
;
325 InflaterCache
.release(inf
);
333 dstPack
.setReadOnly();
335 dstIdx
.setReadOnly();
336 } catch (IOException err
) {
345 private void resolveDeltas(final ProgressMonitor progress
)
347 progress
.beginTask(PROGRESS_RESOLVE_DELTA
, deltaCount
);
348 final int last
= entryCount
;
349 for (int i
= 0; i
< last
; i
++) {
350 final int before
= entryCount
;
351 resolveDeltas(entries
[i
]);
352 progress
.update(entryCount
- before
);
353 if (progress
.isCancelled())
354 throw new IOException("Download cancelled during indexing");
359 private void resolveDeltas(final PackedObjectInfo oe
) throws IOException
{
360 final int oldCRC
= oe
.getCRC();
361 if (baseById
.containsKey(oe
)
362 || baseByPos
.containsKey(new Long(oe
.getOffset())))
363 resolveDeltas(oe
.getOffset(), oldCRC
, Constants
.OBJ_BAD
, null, oe
);
366 private void resolveDeltas(final long pos
, final int oldCRC
, int type
,
367 byte[] data
, PackedObjectInfo oe
) throws IOException
{
370 int c
= readFromFile();
371 final int typeCode
= (c
>> 4) & 7;
374 while ((c
& 0x80) != 0) {
376 sz
+= (c
& 0x7f) << shift
;
381 case Constants
.OBJ_COMMIT
:
382 case Constants
.OBJ_TREE
:
383 case Constants
.OBJ_BLOB
:
384 case Constants
.OBJ_TAG
:
386 data
= inflateFromFile((int) sz
);
388 case Constants
.OBJ_OFS_DELTA
: {
389 c
= readFromFile() & 0xff;
390 while ((c
& 128) != 0)
391 c
= readFromFile() & 0xff;
392 data
= BinaryDelta
.apply(data
, inflateFromFile((int) sz
));
395 case Constants
.OBJ_REF_DELTA
: {
396 crc
.update(buf
, fillFromFile(20), 20);
398 data
= BinaryDelta
.apply(data
, inflateFromFile((int) sz
));
402 throw new IOException("Unknown object type " + typeCode
+ ".");
405 final int crc32
= (int) crc
.getValue();
407 throw new IOException("Corruption detected re-reading at " + pos
);
409 objectDigest
.update(Constants
.encodedTypeString(type
));
410 objectDigest
.update((byte) ' ');
411 objectDigest
.update(Constants
.encodeASCII(data
.length
));
412 objectDigest
.update((byte) 0);
413 objectDigest
.update(data
);
414 tempObjectId
.fromRaw(objectDigest
.digest(), 0);
416 verifySafeObject(tempObjectId
, type
, data
);
417 oe
= new PackedObjectInfo(pos
, crc32
, tempObjectId
);
418 entries
[entryCount
++] = oe
;
421 resolveChildDeltas(pos
, type
, data
, oe
);
424 private void resolveChildDeltas(final long pos
, int type
, byte[] data
,
425 PackedObjectInfo oe
) throws IOException
{
426 final ArrayList
<UnresolvedDelta
> a
= baseById
.remove(oe
);
427 final ArrayList
<UnresolvedDelta
> b
= baseByPos
.remove(new Long(pos
));
429 if (a
!= null && b
!= null) {
430 while (ai
< a
.size() && bi
< b
.size()) {
431 final UnresolvedDelta ad
= a
.get(ai
);
432 final UnresolvedDelta bd
= b
.get(bi
);
433 if (ad
.position
< bd
.position
) {
434 resolveDeltas(ad
.position
, ad
.crc
, type
, data
, null);
437 resolveDeltas(bd
.position
, bd
.crc
, type
, data
, null);
443 while (ai
< a
.size()) {
444 final UnresolvedDelta ad
= a
.get(ai
++);
445 resolveDeltas(ad
.position
, ad
.crc
, type
, data
, null);
448 while (bi
< b
.size()) {
449 final UnresolvedDelta bd
= b
.get(bi
++);
450 resolveDeltas(bd
.position
, bd
.crc
, type
, data
, null);
454 private void fixThinPack(final ProgressMonitor progress
) throws IOException
{
458 originalEOF
= packOut
.length() - 20;
459 final Deflater def
= new Deflater(Deflater
.DEFAULT_COMPRESSION
, false);
460 long end
= originalEOF
;
461 for (final ObjectId baseId
: new ArrayList
<ObjectId
>(baseById
.keySet())) {
462 final ObjectLoader ldr
= repo
.openObject(baseId
);
465 final byte[] data
= ldr
.getBytes();
466 final int typeCode
= ldr
.getType();
467 final PackedObjectInfo oe
;
471 writeWhole(def
, typeCode
, data
);
472 oe
= new PackedObjectInfo(end
, (int) crc
.getValue(), baseId
);
473 entries
[entryCount
++] = oe
;
474 end
= packOut
.getFilePointer();
476 resolveChildDeltas(oe
.getOffset(), typeCode
, data
, oe
);
477 if (progress
.isCancelled())
478 throw new IOException("Download cancelled during indexing");
482 if (!baseById
.isEmpty()) {
483 final ObjectId need
= baseById
.keySet().iterator().next();
484 throw new MissingObjectException(need
, "delta base");
487 fixHeaderFooter(packcsum
, packDigest
.digest());
490 private void writeWhole(final Deflater def
, final int typeCode
,
491 final byte[] data
) throws IOException
{
492 int sz
= data
.length
;
494 buf
[hdrlen
++] = (byte) ((typeCode
<< 4) | sz
& 15);
497 buf
[hdrlen
- 1] |= 0x80;
498 buf
[hdrlen
++] = (byte) (sz
& 0x7f);
501 packDigest
.update(buf
, 0, hdrlen
);
502 crc
.update(buf
, 0, hdrlen
);
503 packOut
.write(buf
, 0, hdrlen
);
507 while (!def
.finished()) {
508 final int datlen
= def
.deflate(buf
);
509 packDigest
.update(buf
, 0, datlen
);
510 crc
.update(buf
, 0, datlen
);
511 packOut
.write(buf
, 0, datlen
);
515 private void fixHeaderFooter(final byte[] origcsum
, final byte[] tailcsum
)
517 final MessageDigest origDigest
= Constants
.newMessageDigest();
518 final MessageDigest tailDigest
= Constants
.newMessageDigest();
519 long origRemaining
= originalEOF
;
527 final int origCnt
= (int) Math
.min(bAvail
, origRemaining
);
528 origDigest
.update(buf
, 0, origCnt
);
529 origRemaining
-= origCnt
;
530 if (origRemaining
== 0)
531 tailDigest
.update(buf
, origCnt
, bAvail
- origCnt
);
534 NB
.encodeInt32(buf
, 8, entryCount
);
536 packOut
.write(buf
, 0, 12);
537 packOut
.seek(bAvail
);
540 packDigest
.update(buf
, 0, bAvail
);
542 final int n
= packOut
.read(buf
);
545 if (origRemaining
!= 0) {
546 final int origCnt
= (int) Math
.min(n
, origRemaining
);
547 origDigest
.update(buf
, 0, origCnt
);
548 origRemaining
-= origCnt
;
549 if (origRemaining
== 0)
550 tailDigest
.update(buf
, origCnt
, n
- origCnt
);
552 tailDigest
.update(buf
, 0, n
);
554 packDigest
.update(buf
, 0, n
);
557 if (!Arrays
.equals(origDigest
.digest(), origcsum
)
558 || !Arrays
.equals(tailDigest
.digest(), tailcsum
))
559 throw new IOException("Pack corrupted while writing to filesystem");
561 packcsum
= packDigest
.digest();
562 packOut
.write(packcsum
);
565 private void growEntries() {
566 final PackedObjectInfo
[] ne
;
568 ne
= new PackedObjectInfo
[(int) objectCount
+ baseById
.size()];
569 System
.arraycopy(entries
, 0, ne
, 0, entryCount
);
573 private void writeIdx() throws IOException
{
574 Arrays
.sort(entries
, 0, entryCount
);
575 List
<PackedObjectInfo
> list
= Arrays
.asList(entries
);
576 if (entryCount
< entries
.length
)
577 list
= list
.subList(0, entryCount
);
579 final FileOutputStream os
= new FileOutputStream(dstIdx
);
581 final PackIndexWriter iw
;
582 if (outputVersion
<= 0)
583 iw
= PackIndexWriter
.createOldestPossible(os
, list
);
585 iw
= PackIndexWriter
.createVersion(os
, outputVersion
);
586 iw
.write(list
, packcsum
);
587 os
.getChannel().force(true);
593 private void readPackHeader() throws IOException
{
594 final int hdrln
= Constants
.PACK_SIGNATURE
.length
+ 4 + 4;
595 final int p
= fillFromInput(hdrln
);
596 for (int k
= 0; k
< Constants
.PACK_SIGNATURE
.length
; k
++)
597 if (buf
[p
+ k
] != Constants
.PACK_SIGNATURE
[k
])
598 throw new IOException("Not a PACK file.");
600 final long vers
= NB
.decodeUInt32(buf
, p
+ 4);
601 if (vers
!= 2 && vers
!= 3)
602 throw new IOException("Unsupported pack version " + vers
+ ".");
603 objectCount
= NB
.decodeUInt32(buf
, p
+ 8);
607 private void readPackFooter() throws IOException
{
609 final byte[] cmpcsum
= packDigest
.digest();
610 final int c
= fillFromInput(20);
611 packcsum
= new byte[20];
612 System
.arraycopy(buf
, c
, packcsum
, 0, 20);
615 packOut
.write(packcsum
);
617 if (!Arrays
.equals(cmpcsum
, packcsum
))
618 throw new CorruptObjectException("Packfile checksum incorrect.");
621 // Cleanup all resources associated with our input parsing.
622 private void endInput() {
627 // Read one entire object or delta from the input.
628 private void indexOneObject() throws IOException
{
629 final long pos
= position();
632 int c
= readFromInput();
633 final int typeCode
= (c
>> 4) & 7;
636 while ((c
& 0x80) != 0) {
638 sz
+= (c
& 0x7f) << shift
;
643 case Constants
.OBJ_COMMIT
:
644 case Constants
.OBJ_TREE
:
645 case Constants
.OBJ_BLOB
:
646 case Constants
.OBJ_TAG
:
647 whole(typeCode
, pos
, sz
);
649 case Constants
.OBJ_OFS_DELTA
: {
652 while ((c
& 128) != 0) {
658 final Long base
= new Long(pos
- ofs
);
659 ArrayList
<UnresolvedDelta
> r
= baseByPos
.get(base
);
661 r
= new ArrayList
<UnresolvedDelta
>(8);
662 baseByPos
.put(base
, r
);
664 skipInflateFromInput(sz
);
665 r
.add(new UnresolvedDelta(pos
, (int) crc
.getValue()));
669 case Constants
.OBJ_REF_DELTA
: {
670 c
= fillFromInput(20);
671 crc
.update(buf
, c
, 20);
672 final ObjectId base
= ObjectId
.fromRaw(buf
, c
);
674 ArrayList
<UnresolvedDelta
> r
= baseById
.get(base
);
676 r
= new ArrayList
<UnresolvedDelta
>(8);
677 baseById
.put(base
, r
);
679 skipInflateFromInput(sz
);
680 r
.add(new UnresolvedDelta(pos
, (int) crc
.getValue()));
685 throw new IOException("Unknown object type " + typeCode
+ ".");
689 private void whole(final int type
, final long pos
, final long sz
)
691 final byte[] data
= inflateFromInput(sz
);
692 objectDigest
.update(Constants
.encodedTypeString(type
));
693 objectDigest
.update((byte) ' ');
694 objectDigest
.update(Constants
.encodeASCII(sz
));
695 objectDigest
.update((byte) 0);
696 objectDigest
.update(data
);
697 tempObjectId
.fromRaw(objectDigest
.digest(), 0);
699 verifySafeObject(tempObjectId
, type
, data
);
700 final int crc32
= (int) crc
.getValue();
701 entries
[entryCount
++] = new PackedObjectInfo(pos
, crc32
, tempObjectId
);
704 private void verifySafeObject(final AnyObjectId id
, final int type
,
705 final byte[] data
) throws IOException
{
706 if (objCheck
!= null) {
708 objCheck
.check(type
, data
);
709 } catch (CorruptObjectException e
) {
710 throw new IOException("Invalid "
711 + Constants
.encodedTypeString(type
) + " " + id
.name()
712 + ":" + e
.getMessage());
716 final ObjectLoader ldr
= repo
.openObject(id
);
718 final byte[] existingData
= ldr
.getCachedBytes();
719 if (ldr
.getType() != type
|| !Arrays
.equals(data
, existingData
)) {
720 throw new IOException("Collision on " + id
.name());
725 // Current position of {@link #bOffset} within the entire file.
726 private long position() {
727 return bBase
+ bOffset
;
730 private void position(final long pos
) throws IOException
{
737 // Consume exactly one byte from the buffer and return it.
738 private int readFromInput() throws IOException
{
742 final int b
= buf
[bOffset
++] & 0xff;
747 // Consume exactly one byte from the buffer and return it.
748 private int readFromFile() throws IOException
{
752 final int b
= buf
[bOffset
++] & 0xff;
757 // Consume cnt bytes from the buffer.
758 private void use(final int cnt
) {
763 // Ensure at least need bytes are available in in {@link #buf}.
764 private int fillFromInput(final int need
) throws IOException
{
765 while (bAvail
< need
) {
766 int next
= bOffset
+ bAvail
;
767 int free
= buf
.length
- next
;
768 if (free
+ bAvail
< need
) {
771 free
= buf
.length
- next
;
773 next
= in
.read(buf
, next
, free
);
775 throw new EOFException("Packfile is truncated.");
781 // Ensure at least need bytes are available in in {@link #buf}.
782 private int fillFromFile(final int need
) throws IOException
{
784 int next
= bOffset
+ bAvail
;
785 int free
= buf
.length
- next
;
786 if (free
+ bAvail
< need
) {
788 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
791 free
= buf
.length
- next
;
793 next
= packOut
.read(buf
, next
, free
);
795 throw new EOFException("Packfile is truncated.");
801 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
802 private void sync() throws IOException
{
803 packDigest
.update(buf
, 0, bOffset
);
805 packOut
.write(buf
, 0, bOffset
);
807 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
812 private void skipInflateFromInput(long sz
) throws IOException
{
813 final Inflater inf
= inflater
;
815 final byte[] dst
= objectData
;
818 while (!inf
.finished()) {
819 if (inf
.needsInput()) {
821 crc
.update(buf
, p
, bAvail
);
824 p
= fillFromInput(1);
825 inf
.setInput(buf
, p
, bAvail
);
828 int free
= dst
.length
- n
;
834 n
+= inf
.inflate(dst
, n
, free
);
837 throw new DataFormatException("wrong decompressed length");
838 n
= bAvail
- inf
.getRemaining();
840 crc
.update(buf
, p
, n
);
843 } catch (DataFormatException dfe
) {
850 private byte[] inflateFromInput(final long sz
) throws IOException
{
851 final byte[] dst
= new byte[(int) sz
];
852 final Inflater inf
= inflater
;
856 while (!inf
.finished()) {
857 if (inf
.needsInput()) {
859 crc
.update(buf
, p
, bAvail
);
862 p
= fillFromInput(1);
863 inf
.setInput(buf
, p
, bAvail
);
866 n
+= inf
.inflate(dst
, n
, dst
.length
- n
);
869 throw new DataFormatException("wrong decompressed length");
870 n
= bAvail
- inf
.getRemaining();
872 crc
.update(buf
, p
, n
);
876 } catch (DataFormatException dfe
) {
883 private byte[] inflateFromFile(final int sz
) throws IOException
{
884 final Inflater inf
= inflater
;
886 final byte[] dst
= new byte[sz
];
889 while (!inf
.finished()) {
890 if (inf
.needsInput()) {
892 crc
.update(buf
, p
, bAvail
);
896 inf
.setInput(buf
, p
, bAvail
);
898 n
+= inf
.inflate(dst
, n
, sz
- n
);
900 n
= bAvail
- inf
.getRemaining();
902 crc
.update(buf
, p
, n
);
906 } catch (DataFormatException dfe
) {
913 private static CorruptObjectException
corrupt(final DataFormatException dfe
) {
914 return new CorruptObjectException("Packfile corruption detected: "
918 private static class UnresolvedDelta
{
923 UnresolvedDelta(final long headerOffset
, final int crc32
) {
924 position
= headerOffset
;
930 * Rename the pack to it's final name and location and open it.
932 * If the call completes successfully the repository this IndexPack instance
933 * was created with will have the objects in the pack available for reading
934 * and use, without needing to scan for packs.
936 * @throws IOException
937 * The pack could not be inserted into the repository's objects
938 * directory. The pack no longer exists on disk, as it was
939 * removed prior to throwing the exception to the caller.
941 public void renameAndOpenPack() throws IOException
{
942 final MessageDigest d
= Constants
.newMessageDigest();
943 final byte[] oeBytes
= new byte[Constants
.OBJECT_ID_LENGTH
];
944 for (int i
= 0; i
< entryCount
; i
++) {
945 final PackedObjectInfo oe
= entries
[i
];
946 oe
.copyRawTo(oeBytes
, 0);
950 final String name
= ObjectId
.fromRaw(d
.digest()).name();
951 final File packDir
= new File(repo
.getObjectsDirectory(), "pack");
952 final File finalPack
= new File(packDir
, "pack-" + name
+ ".pack");
953 final File finalIdx
= new File(packDir
, "pack-" + name
+ ".idx");
955 if (finalPack
.exists()) {
956 // If the pack is already present we should never replace it.
958 cleanupTemporaryFiles();
962 if (!dstPack
.renameTo(finalPack
)) {
963 cleanupTemporaryFiles();
964 throw new IOException("Cannot move pack to " + finalPack
);
967 if (!dstIdx
.renameTo(finalIdx
)) {
968 cleanupTemporaryFiles();
969 if (!finalPack
.delete())
970 finalPack
.deleteOnExit();
971 throw new IOException("Cannot move index to " + finalIdx
);
975 repo
.openPack(finalPack
, finalIdx
);
976 } catch (IOException err
) {
983 private void cleanupTemporaryFiles() {
984 if (!dstIdx
.delete())
985 dstIdx
.deleteOnExit();
986 if (!dstPack
.delete())
987 dstPack
.deleteOnExit();