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
.ObjectIdMap
;
66 import org
.spearce
.jgit
.lib
.ObjectLoader
;
67 import org
.spearce
.jgit
.lib
.PackIndexWriter
;
68 import org
.spearce
.jgit
.lib
.ProgressMonitor
;
69 import org
.spearce
.jgit
.lib
.Repository
;
70 import org
.spearce
.jgit
.lib
.WindowCursor
;
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 boolean keepEmpty
;
147 private int outputVersion
;
149 private final File dstPack
;
151 private final File dstIdx
;
153 private long objectCount
;
155 private PackedObjectInfo
[] entries
;
157 private int deltaCount
;
159 private int entryCount
;
161 private final CRC32 crc
= new CRC32();
163 private ObjectIdMap
<ArrayList
<UnresolvedDelta
>> baseById
;
165 private LongMap
<ArrayList
<UnresolvedDelta
>> baseByPos
;
167 private byte[] objectData
;
169 private MessageDigest packDigest
;
171 private RandomAccessFile packOut
;
173 private byte[] packcsum
;
175 /** If {@link #fixThin} this is the last byte of the original checksum. */
176 private long originalEOF
;
178 private WindowCursor readCurs
;
181 * Create a new pack indexer utility.
185 * stream to read the pack data from. If the stream is buffered
186 * use {@link #BUFFER_SIZE} as the buffer size for the stream.
188 * @throws IOException
189 * the output packfile could not be created.
191 public IndexPack(final Repository db
, final InputStream src
,
192 final File dstBase
) throws IOException
{
195 inflater
= InflaterCache
.get();
196 readCurs
= new WindowCursor();
197 buf
= new byte[BUFFER_SIZE
];
198 objectData
= new byte[BUFFER_SIZE
];
199 objectDigest
= Constants
.newMessageDigest();
200 tempObjectId
= new MutableObjectId();
201 packDigest
= Constants
.newMessageDigest();
203 if (dstBase
!= null) {
204 final File dir
= dstBase
.getParentFile();
205 final String nam
= dstBase
.getName();
206 dstPack
= new File(dir
, nam
+ ".pack");
207 dstIdx
= new File(dir
, nam
+ ".idx");
208 packOut
= new RandomAccessFile(dstPack
, "rw");
209 packOut
.setLength(0);
217 * Set the pack index file format version this instance will create.
220 * the version to write. The special version 0 designates the
221 * oldest (most compatible) format available for the objects.
222 * @see PackIndexWriter
224 public void setIndexVersion(final int version
) {
225 outputVersion
= version
;
229 * Configure this index pack instance to make a thin pack complete.
231 * Thin packs are sometimes used during network transfers to allow a delta
232 * to be sent without a base object. Such packs are not permitted on disk.
233 * They can be fixed by copying the base object onto the end of the pack.
236 * true to enable fixing a thin pack.
238 public void setFixThin(final boolean fix
) {
243 * Configure this index pack instance to keep an empty pack.
245 * By default an empty pack (a pack with no objects) is not kept, as doing
246 * so is completely pointless. With no objects in the pack there is no data
247 * stored by it, so the pack is unnecessary.
249 * @param empty true to enable keeping an empty pack.
251 public void setKeepEmpty(final boolean empty
) {
256 * Configure the checker used to validate received objects.
258 * Usually object checking isn't necessary, as Git implementations only
259 * create valid objects in pack files. However, additional checking may be
260 * useful if processing data from an untrusted source.
263 * the checker instance; null to disable object checking.
265 public void setObjectChecker(final ObjectChecker oc
) {
270 * Configure the checker used to validate received objects.
272 * Usually object checking isn't necessary, as Git implementations only
273 * create valid objects in pack files. However, additional checking may be
274 * useful if processing data from an untrusted source.
276 * This is shorthand for:
279 * setObjectChecker(on ? new ObjectChecker() : null);
283 * true to enable the default checker; false to disable it.
285 public void setObjectChecking(final boolean on
) {
286 setObjectChecker(on ?
new ObjectChecker() : null);
290 * Consume data from the input stream until the packfile is indexed.
295 * @throws IOException
297 public void index(final ProgressMonitor progress
) throws IOException
{
298 progress
.start(2 /* tasks */);
303 entries
= new PackedObjectInfo
[(int) objectCount
];
304 baseById
= new ObjectIdMap
<ArrayList
<UnresolvedDelta
>>();
305 baseByPos
= new LongMap
<ArrayList
<UnresolvedDelta
>>();
307 progress
.beginTask(PROGRESS_DOWNLOAD
, (int) objectCount
);
308 for (int done
= 0; done
< objectCount
; done
++) {
311 if (progress
.isCancelled())
312 throw new IOException("Download cancelled");
317 if (deltaCount
> 0) {
319 throw new IOException("need packOut");
320 resolveDeltas(progress
);
321 if (entryCount
< objectCount
) {
323 throw new IOException("pack has "
324 + (objectCount
- entryCount
)
325 + " unresolved deltas");
327 fixThinPack(progress
);
330 if (packOut
!= null && (keepEmpty
|| entryCount
> 0))
331 packOut
.getChannel().force(true);
337 if (dstIdx
!= null && (keepEmpty
|| entryCount
> 0))
342 InflaterCache
.release(inflater
);
346 readCurs
= WindowCursor
.release(readCurs
);
353 if (keepEmpty
|| entryCount
> 0) {
355 dstPack
.setReadOnly();
357 dstIdx
.setReadOnly();
359 } catch (IOException err
) {
368 private void resolveDeltas(final ProgressMonitor progress
)
370 progress
.beginTask(PROGRESS_RESOLVE_DELTA
, deltaCount
);
371 final int last
= entryCount
;
372 for (int i
= 0; i
< last
; i
++) {
373 final int before
= entryCount
;
374 resolveDeltas(entries
[i
]);
375 progress
.update(entryCount
- before
);
376 if (progress
.isCancelled())
377 throw new IOException("Download cancelled during indexing");
382 private void resolveDeltas(final PackedObjectInfo oe
) throws IOException
{
383 final int oldCRC
= oe
.getCRC();
384 if (baseById
.containsKey(oe
) || baseByPos
.containsKey(oe
.getOffset()))
385 resolveDeltas(oe
.getOffset(), oldCRC
, Constants
.OBJ_BAD
, null, oe
);
388 private void resolveDeltas(final long pos
, final int oldCRC
, int type
,
389 byte[] data
, PackedObjectInfo oe
) throws IOException
{
392 int c
= readFromFile();
393 final int typeCode
= (c
>> 4) & 7;
396 while ((c
& 0x80) != 0) {
398 sz
+= (c
& 0x7f) << shift
;
403 case Constants
.OBJ_COMMIT
:
404 case Constants
.OBJ_TREE
:
405 case Constants
.OBJ_BLOB
:
406 case Constants
.OBJ_TAG
:
408 data
= inflateFromFile((int) sz
);
410 case Constants
.OBJ_OFS_DELTA
: {
411 c
= readFromFile() & 0xff;
412 while ((c
& 128) != 0)
413 c
= readFromFile() & 0xff;
414 data
= BinaryDelta
.apply(data
, inflateFromFile((int) sz
));
417 case Constants
.OBJ_REF_DELTA
: {
418 crc
.update(buf
, fillFromFile(20), 20);
420 data
= BinaryDelta
.apply(data
, inflateFromFile((int) sz
));
424 throw new IOException("Unknown object type " + typeCode
+ ".");
427 final int crc32
= (int) crc
.getValue();
429 throw new IOException("Corruption detected re-reading at " + pos
);
431 objectDigest
.update(Constants
.encodedTypeString(type
));
432 objectDigest
.update((byte) ' ');
433 objectDigest
.update(Constants
.encodeASCII(data
.length
));
434 objectDigest
.update((byte) 0);
435 objectDigest
.update(data
);
436 tempObjectId
.fromRaw(objectDigest
.digest(), 0);
438 verifySafeObject(tempObjectId
, type
, data
);
439 oe
= new PackedObjectInfo(pos
, crc32
, tempObjectId
);
440 entries
[entryCount
++] = oe
;
443 resolveChildDeltas(pos
, type
, data
, oe
);
446 private void resolveChildDeltas(final long pos
, int type
, byte[] data
,
447 PackedObjectInfo oe
) throws IOException
{
448 final ArrayList
<UnresolvedDelta
> a
= baseById
.remove(oe
);
449 final ArrayList
<UnresolvedDelta
> b
= baseByPos
.remove(pos
);
451 if (a
!= null && b
!= null) {
452 while (ai
< a
.size() && bi
< b
.size()) {
453 final UnresolvedDelta ad
= a
.get(ai
);
454 final UnresolvedDelta bd
= b
.get(bi
);
455 if (ad
.position
< bd
.position
) {
456 resolveDeltas(ad
.position
, ad
.crc
, type
, data
, null);
459 resolveDeltas(bd
.position
, bd
.crc
, type
, data
, null);
465 while (ai
< a
.size()) {
466 final UnresolvedDelta ad
= a
.get(ai
++);
467 resolveDeltas(ad
.position
, ad
.crc
, type
, data
, null);
470 while (bi
< b
.size()) {
471 final UnresolvedDelta bd
= b
.get(bi
++);
472 resolveDeltas(bd
.position
, bd
.crc
, type
, data
, null);
476 private void fixThinPack(final ProgressMonitor progress
) throws IOException
{
480 originalEOF
= packOut
.length() - 20;
481 final Deflater def
= new Deflater(Deflater
.DEFAULT_COMPRESSION
, false);
482 long end
= originalEOF
;
483 for (final ObjectId baseId
: new ArrayList
<ObjectId
>(baseById
.keySet())) {
484 final ObjectLoader ldr
= repo
.openObject(readCurs
, baseId
);
487 final byte[] data
= ldr
.getBytes();
488 final int typeCode
= ldr
.getType();
489 final PackedObjectInfo oe
;
493 writeWhole(def
, typeCode
, data
);
494 oe
= new PackedObjectInfo(end
, (int) crc
.getValue(), baseId
);
495 entries
[entryCount
++] = oe
;
496 end
= packOut
.getFilePointer();
498 resolveChildDeltas(oe
.getOffset(), typeCode
, data
, oe
);
499 if (progress
.isCancelled())
500 throw new IOException("Download cancelled during indexing");
504 if (!baseById
.isEmpty()) {
505 final ObjectId need
= baseById
.keySet().iterator().next();
506 throw new MissingObjectException(need
, "delta base");
509 fixHeaderFooter(packcsum
, packDigest
.digest());
512 private void writeWhole(final Deflater def
, final int typeCode
,
513 final byte[] data
) throws IOException
{
514 int sz
= data
.length
;
516 buf
[hdrlen
++] = (byte) ((typeCode
<< 4) | sz
& 15);
519 buf
[hdrlen
- 1] |= 0x80;
520 buf
[hdrlen
++] = (byte) (sz
& 0x7f);
523 packDigest
.update(buf
, 0, hdrlen
);
524 crc
.update(buf
, 0, hdrlen
);
525 packOut
.write(buf
, 0, hdrlen
);
529 while (!def
.finished()) {
530 final int datlen
= def
.deflate(buf
);
531 packDigest
.update(buf
, 0, datlen
);
532 crc
.update(buf
, 0, datlen
);
533 packOut
.write(buf
, 0, datlen
);
537 private void fixHeaderFooter(final byte[] origcsum
, final byte[] tailcsum
)
539 final MessageDigest origDigest
= Constants
.newMessageDigest();
540 final MessageDigest tailDigest
= Constants
.newMessageDigest();
541 long origRemaining
= originalEOF
;
549 final int origCnt
= (int) Math
.min(bAvail
, origRemaining
);
550 origDigest
.update(buf
, 0, origCnt
);
551 origRemaining
-= origCnt
;
552 if (origRemaining
== 0)
553 tailDigest
.update(buf
, origCnt
, bAvail
- origCnt
);
556 NB
.encodeInt32(buf
, 8, entryCount
);
558 packOut
.write(buf
, 0, 12);
559 packOut
.seek(bAvail
);
562 packDigest
.update(buf
, 0, bAvail
);
564 final int n
= packOut
.read(buf
);
567 if (origRemaining
!= 0) {
568 final int origCnt
= (int) Math
.min(n
, origRemaining
);
569 origDigest
.update(buf
, 0, origCnt
);
570 origRemaining
-= origCnt
;
571 if (origRemaining
== 0)
572 tailDigest
.update(buf
, origCnt
, n
- origCnt
);
574 tailDigest
.update(buf
, 0, n
);
576 packDigest
.update(buf
, 0, n
);
579 if (!Arrays
.equals(origDigest
.digest(), origcsum
)
580 || !Arrays
.equals(tailDigest
.digest(), tailcsum
))
581 throw new IOException("Pack corrupted while writing to filesystem");
583 packcsum
= packDigest
.digest();
584 packOut
.write(packcsum
);
587 private void growEntries() {
588 final PackedObjectInfo
[] ne
;
590 ne
= new PackedObjectInfo
[(int) objectCount
+ baseById
.size()];
591 System
.arraycopy(entries
, 0, ne
, 0, entryCount
);
595 private void writeIdx() throws IOException
{
596 Arrays
.sort(entries
, 0, entryCount
);
597 List
<PackedObjectInfo
> list
= Arrays
.asList(entries
);
598 if (entryCount
< entries
.length
)
599 list
= list
.subList(0, entryCount
);
601 final FileOutputStream os
= new FileOutputStream(dstIdx
);
603 final PackIndexWriter iw
;
604 if (outputVersion
<= 0)
605 iw
= PackIndexWriter
.createOldestPossible(os
, list
);
607 iw
= PackIndexWriter
.createVersion(os
, outputVersion
);
608 iw
.write(list
, packcsum
);
609 os
.getChannel().force(true);
615 private void readPackHeader() throws IOException
{
616 final int hdrln
= Constants
.PACK_SIGNATURE
.length
+ 4 + 4;
617 final int p
= fillFromInput(hdrln
);
618 for (int k
= 0; k
< Constants
.PACK_SIGNATURE
.length
; k
++)
619 if (buf
[p
+ k
] != Constants
.PACK_SIGNATURE
[k
])
620 throw new IOException("Not a PACK file.");
622 final long vers
= NB
.decodeUInt32(buf
, p
+ 4);
623 if (vers
!= 2 && vers
!= 3)
624 throw new IOException("Unsupported pack version " + vers
+ ".");
625 objectCount
= NB
.decodeUInt32(buf
, p
+ 8);
629 private void readPackFooter() throws IOException
{
631 final byte[] cmpcsum
= packDigest
.digest();
632 final int c
= fillFromInput(20);
633 packcsum
= new byte[20];
634 System
.arraycopy(buf
, c
, packcsum
, 0, 20);
637 packOut
.write(packcsum
);
639 if (!Arrays
.equals(cmpcsum
, packcsum
))
640 throw new CorruptObjectException("Packfile checksum incorrect.");
643 // Cleanup all resources associated with our input parsing.
644 private void endInput() {
649 // Read one entire object or delta from the input.
650 private void indexOneObject() throws IOException
{
651 final long pos
= position();
654 int c
= readFromInput();
655 final int typeCode
= (c
>> 4) & 7;
658 while ((c
& 0x80) != 0) {
660 sz
+= (c
& 0x7f) << shift
;
665 case Constants
.OBJ_COMMIT
:
666 case Constants
.OBJ_TREE
:
667 case Constants
.OBJ_BLOB
:
668 case Constants
.OBJ_TAG
:
669 whole(typeCode
, pos
, sz
);
671 case Constants
.OBJ_OFS_DELTA
: {
674 while ((c
& 128) != 0) {
680 final long base
= pos
- ofs
;
681 ArrayList
<UnresolvedDelta
> r
= baseByPos
.get(base
);
683 r
= new ArrayList
<UnresolvedDelta
>(8);
684 baseByPos
.put(base
, r
);
686 skipInflateFromInput(sz
);
687 r
.add(new UnresolvedDelta(pos
, (int) crc
.getValue()));
691 case Constants
.OBJ_REF_DELTA
: {
692 c
= fillFromInput(20);
693 crc
.update(buf
, c
, 20);
694 final ObjectId base
= ObjectId
.fromRaw(buf
, c
);
696 ArrayList
<UnresolvedDelta
> r
= baseById
.get(base
);
698 r
= new ArrayList
<UnresolvedDelta
>(8);
699 baseById
.put(base
, r
);
701 skipInflateFromInput(sz
);
702 r
.add(new UnresolvedDelta(pos
, (int) crc
.getValue()));
707 throw new IOException("Unknown object type " + typeCode
+ ".");
711 private void whole(final int type
, final long pos
, final long sz
)
713 final byte[] data
= inflateFromInput(sz
);
714 objectDigest
.update(Constants
.encodedTypeString(type
));
715 objectDigest
.update((byte) ' ');
716 objectDigest
.update(Constants
.encodeASCII(sz
));
717 objectDigest
.update((byte) 0);
718 objectDigest
.update(data
);
719 tempObjectId
.fromRaw(objectDigest
.digest(), 0);
721 verifySafeObject(tempObjectId
, type
, data
);
722 final int crc32
= (int) crc
.getValue();
723 entries
[entryCount
++] = new PackedObjectInfo(pos
, crc32
, tempObjectId
);
726 private void verifySafeObject(final AnyObjectId id
, final int type
,
727 final byte[] data
) throws IOException
{
728 if (objCheck
!= null) {
730 objCheck
.check(type
, data
);
731 } catch (CorruptObjectException e
) {
732 throw new IOException("Invalid "
733 + Constants
.encodedTypeString(type
) + " " + id
.name()
734 + ":" + e
.getMessage());
738 final ObjectLoader ldr
= repo
.openObject(readCurs
, id
);
740 final byte[] existingData
= ldr
.getCachedBytes();
741 if (ldr
.getType() != type
|| !Arrays
.equals(data
, existingData
)) {
742 throw new IOException("Collision on " + id
.name());
747 // Current position of {@link #bOffset} within the entire file.
748 private long position() {
749 return bBase
+ bOffset
;
752 private void position(final long pos
) throws IOException
{
759 // Consume exactly one byte from the buffer and return it.
760 private int readFromInput() throws IOException
{
764 final int b
= buf
[bOffset
++] & 0xff;
769 // Consume exactly one byte from the buffer and return it.
770 private int readFromFile() throws IOException
{
774 final int b
= buf
[bOffset
++] & 0xff;
779 // Consume cnt bytes from the buffer.
780 private void use(final int cnt
) {
785 // Ensure at least need bytes are available in in {@link #buf}.
786 private int fillFromInput(final int need
) throws IOException
{
787 while (bAvail
< need
) {
788 int next
= bOffset
+ bAvail
;
789 int free
= buf
.length
- next
;
790 if (free
+ bAvail
< need
) {
793 free
= buf
.length
- next
;
795 next
= in
.read(buf
, next
, free
);
797 throw new EOFException("Packfile is truncated.");
803 // Ensure at least need bytes are available in in {@link #buf}.
804 private int fillFromFile(final int need
) throws IOException
{
806 int next
= bOffset
+ bAvail
;
807 int free
= buf
.length
- next
;
808 if (free
+ bAvail
< need
) {
810 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
813 free
= buf
.length
- next
;
815 next
= packOut
.read(buf
, next
, free
);
817 throw new EOFException("Packfile is truncated.");
823 // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
824 private void sync() throws IOException
{
825 packDigest
.update(buf
, 0, bOffset
);
827 packOut
.write(buf
, 0, bOffset
);
829 System
.arraycopy(buf
, bOffset
, buf
, 0, bAvail
);
834 private void skipInflateFromInput(long sz
) throws IOException
{
835 final Inflater inf
= inflater
;
837 final byte[] dst
= objectData
;
840 while (!inf
.finished()) {
841 if (inf
.needsInput()) {
843 crc
.update(buf
, p
, bAvail
);
846 p
= fillFromInput(1);
847 inf
.setInput(buf
, p
, bAvail
);
850 int free
= dst
.length
- n
;
856 n
+= inf
.inflate(dst
, n
, free
);
859 throw new DataFormatException("wrong decompressed length");
860 n
= bAvail
- inf
.getRemaining();
862 crc
.update(buf
, p
, n
);
865 } catch (DataFormatException dfe
) {
872 private byte[] inflateFromInput(final long sz
) throws IOException
{
873 final byte[] dst
= new byte[(int) sz
];
874 final Inflater inf
= inflater
;
878 while (!inf
.finished()) {
879 if (inf
.needsInput()) {
881 crc
.update(buf
, p
, bAvail
);
884 p
= fillFromInput(1);
885 inf
.setInput(buf
, p
, bAvail
);
888 n
+= inf
.inflate(dst
, n
, dst
.length
- n
);
891 throw new DataFormatException("wrong decompressed length");
892 n
= bAvail
- inf
.getRemaining();
894 crc
.update(buf
, p
, n
);
898 } catch (DataFormatException dfe
) {
905 private byte[] inflateFromFile(final int sz
) throws IOException
{
906 final Inflater inf
= inflater
;
908 final byte[] dst
= new byte[sz
];
911 while (!inf
.finished()) {
912 if (inf
.needsInput()) {
914 crc
.update(buf
, p
, bAvail
);
918 inf
.setInput(buf
, p
, bAvail
);
920 n
+= inf
.inflate(dst
, n
, sz
- n
);
922 n
= bAvail
- inf
.getRemaining();
924 crc
.update(buf
, p
, n
);
928 } catch (DataFormatException dfe
) {
935 private static CorruptObjectException
corrupt(final DataFormatException dfe
) {
936 return new CorruptObjectException("Packfile corruption detected: "
940 private static class UnresolvedDelta
{
945 UnresolvedDelta(final long headerOffset
, final int crc32
) {
946 position
= headerOffset
;
952 * Rename the pack to it's final name and location and open it.
954 * If the call completes successfully the repository this IndexPack instance
955 * was created with will have the objects in the pack available for reading
956 * and use, without needing to scan for packs.
958 * @throws IOException
959 * The pack could not be inserted into the repository's objects
960 * directory. The pack no longer exists on disk, as it was
961 * removed prior to throwing the exception to the caller.
963 public void renameAndOpenPack() throws IOException
{
964 if (!keepEmpty
&& entryCount
== 0) {
965 cleanupTemporaryFiles();
969 final MessageDigest d
= Constants
.newMessageDigest();
970 final byte[] oeBytes
= new byte[Constants
.OBJECT_ID_LENGTH
];
971 for (int i
= 0; i
< entryCount
; i
++) {
972 final PackedObjectInfo oe
= entries
[i
];
973 oe
.copyRawTo(oeBytes
, 0);
977 final String name
= ObjectId
.fromRaw(d
.digest()).name();
978 final File packDir
= new File(repo
.getObjectsDirectory(), "pack");
979 final File finalPack
= new File(packDir
, "pack-" + name
+ ".pack");
980 final File finalIdx
= new File(packDir
, "pack-" + name
+ ".idx");
982 if (finalPack
.exists()) {
983 // If the pack is already present we should never replace it.
985 cleanupTemporaryFiles();
989 if (!dstPack
.renameTo(finalPack
)) {
990 cleanupTemporaryFiles();
991 throw new IOException("Cannot move pack to " + finalPack
);
994 if (!dstIdx
.renameTo(finalIdx
)) {
995 cleanupTemporaryFiles();
996 if (!finalPack
.delete())
997 finalPack
.deleteOnExit();
998 throw new IOException("Cannot move index to " + finalIdx
);
1002 repo
.openPack(finalPack
, finalIdx
);
1003 } catch (IOException err
) {
1010 private void cleanupTemporaryFiles() {
1011 if (!dstIdx
.delete())
1012 dstIdx
.deleteOnExit();
1013 if (!dstPack
.delete())
1014 dstPack
.deleteOnExit();