1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.transport;
47
48 import java.io.EOFException;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.security.MessageDigest;
52 import java.text.MessageFormat;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Comparator;
56 import java.util.List;
57 import java.util.concurrent.TimeUnit;
58 import java.util.zip.DataFormatException;
59 import java.util.zip.Inflater;
60
61 import org.eclipse.jgit.errors.CorruptObjectException;
62 import org.eclipse.jgit.errors.MissingObjectException;
63 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
64 import org.eclipse.jgit.internal.JGitText;
65 import org.eclipse.jgit.internal.storage.file.PackLock;
66 import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
67 import org.eclipse.jgit.lib.AnyObjectId;
68 import org.eclipse.jgit.lib.BatchingProgressMonitor;
69 import org.eclipse.jgit.lib.Constants;
70 import org.eclipse.jgit.lib.InflaterCache;
71 import org.eclipse.jgit.lib.MutableObjectId;
72 import org.eclipse.jgit.lib.NullProgressMonitor;
73 import org.eclipse.jgit.lib.ObjectChecker;
74 import org.eclipse.jgit.lib.ObjectDatabase;
75 import org.eclipse.jgit.lib.ObjectId;
76 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
77 import org.eclipse.jgit.lib.ObjectIdSubclassMap;
78 import org.eclipse.jgit.lib.ObjectInserter;
79 import org.eclipse.jgit.lib.ObjectLoader;
80 import org.eclipse.jgit.lib.ObjectReader;
81 import org.eclipse.jgit.lib.ObjectStream;
82 import org.eclipse.jgit.lib.ProgressMonitor;
83 import org.eclipse.jgit.util.BlockList;
84 import org.eclipse.jgit.util.IO;
85 import org.eclipse.jgit.util.NB;
86
87
88
89
90
91
92
93
94
95
96
97 public abstract class PackParser {
98
99 private static final int BUFFER_SIZE = 8192;
100
101
102 public static enum Source {
103
104 INPUT,
105
106
107 DATABASE;
108 }
109
110
111 private final ObjectDatabase objectDatabase;
112
113 private InflaterStream inflater;
114
115 private byte[] tempBuffer;
116
117 private byte[] hdrBuf;
118
119 private final MessageDigest objectDigest;
120
121 private final MutableObjectId tempObjectId;
122
123 private InputStream in;
124
125 byte[] buf;
126
127
128 private long bBase;
129
130 private int bOffset;
131
132 int bAvail;
133
134 private ObjectChecker objCheck;
135
136 private boolean allowThin;
137
138 private boolean checkObjectCollisions;
139
140 private boolean needBaseObjectIds;
141
142 private boolean checkEofAfterPackFooter;
143
144 private boolean expectDataAfterPackFooter;
145
146 private long objectCount;
147
148 private PackedObjectInfo[] entries;
149
150
151
152
153
154
155
156
157 private ObjectIdSubclassMap<ObjectId> newObjectIds;
158
159 private int deltaCount;
160
161 private int entryCount;
162
163 private ObjectIdOwnerMap<DeltaChain> baseById;
164
165
166
167
168
169
170
171
172 private ObjectIdSubclassMap<ObjectId> baseObjectIds;
173
174 private LongMap<UnresolvedDelta> baseByPos;
175
176
177 private BlockList<PackedObjectInfo> deferredCheckBlobs;
178
179 private MessageDigest packDigest;
180
181 private ObjectReader readCurs;
182
183
184 private String lockMessage;
185
186
187 private long maxObjectSizeLimit;
188
189
190
191
192
193
194
195
196
197 protected PackParser(final ObjectDatabase odb, final InputStream src) {
198 objectDatabase = odb.newCachedDatabase();
199 in = src;
200
201 inflater = new InflaterStream();
202 readCurs = objectDatabase.newReader();
203 buf = new byte[BUFFER_SIZE];
204 tempBuffer = new byte[BUFFER_SIZE];
205 hdrBuf = new byte[64];
206 objectDigest = Constants.newMessageDigest();
207 tempObjectId = new MutableObjectId();
208 packDigest = Constants.newMessageDigest();
209 checkObjectCollisions = true;
210 }
211
212
213 public boolean isAllowThin() {
214 return allowThin;
215 }
216
217
218
219
220
221
222
223
224
225
226 public void setAllowThin(final boolean allow) {
227 allowThin = allow;
228 }
229
230
231
232
233
234 protected boolean isCheckObjectCollisions() {
235 return checkObjectCollisions;
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 protected void setCheckObjectCollisions(boolean check) {
260 checkObjectCollisions = check;
261 }
262
263
264
265
266
267
268
269
270
271
272
273 public void setNeedNewObjectIds(boolean b) {
274 if (b)
275 newObjectIds = new ObjectIdSubclassMap<ObjectId>();
276 else
277 newObjectIds = null;
278 }
279
280 private boolean needNewObjectIds() {
281 return newObjectIds != null;
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295 public void setNeedBaseObjectIds(boolean b) {
296 this.needBaseObjectIds = b;
297 }
298
299
300 public boolean isCheckEofAfterPackFooter() {
301 return checkEofAfterPackFooter;
302 }
303
304
305
306
307
308
309
310 public void setCheckEofAfterPackFooter(boolean b) {
311 checkEofAfterPackFooter = b;
312 }
313
314
315 public boolean isExpectDataAfterPackFooter() {
316 return expectDataAfterPackFooter;
317 }
318
319
320
321
322
323
324
325 public void setExpectDataAfterPackFooter(boolean e) {
326 expectDataAfterPackFooter = e;
327 }
328
329
330 public ObjectIdSubclassMap<ObjectId> getNewObjectIds() {
331 if (newObjectIds != null)
332 return newObjectIds;
333 return new ObjectIdSubclassMap<ObjectId>();
334 }
335
336
337 public ObjectIdSubclassMap<ObjectId> getBaseObjectIds() {
338 if (baseObjectIds != null)
339 return baseObjectIds;
340 return new ObjectIdSubclassMap<ObjectId>();
341 }
342
343
344
345
346
347
348
349
350
351
352
353 public void setObjectChecker(final ObjectChecker oc) {
354 objCheck = oc;
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 public void setObjectChecking(final boolean on) {
374 setObjectChecker(on ? new ObjectChecker() : null);
375 }
376
377
378 public String getLockMessage() {
379 return lockMessage;
380 }
381
382
383
384
385
386
387
388
389 public void setLockMessage(String msg) {
390 lockMessage = msg;
391 }
392
393
394
395
396
397
398
399
400
401
402 public void setMaxObjectSizeLimit(long limit) {
403 maxObjectSizeLimit = limit;
404 }
405
406
407
408
409
410
411
412
413
414
415 public int getObjectCount() {
416 return entryCount;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 public PackedObjectInfo getObject(int nth) {
431 return entries[nth];
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448 public List<PackedObjectInfo> getSortedObjectList(
449 Comparator<PackedObjectInfo> cmp) {
450 Arrays.sort(entries, 0, entryCount, cmp);
451 List<PackedObjectInfo> list = Arrays.asList(entries);
452 if (entryCount < entries.length)
453 list = list.subList(0, entryCount);
454 return list;
455 }
456
457
458
459
460
461
462
463
464
465
466
467 public long getPackSize() {
468 return -1;
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482
483 public final PackLock parse(ProgressMonitor progress) throws IOException {
484 return parse(progress, progress);
485 }
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
503 throws IOException {
504 if (receiving == null)
505 receiving = NullProgressMonitor.INSTANCE;
506 if (resolving == null)
507 resolving = NullProgressMonitor.INSTANCE;
508
509 if (receiving == resolving)
510 receiving.start(2 );
511 try {
512 readPackHeader();
513
514 entries = new PackedObjectInfo[(int) objectCount];
515 baseById = new ObjectIdOwnerMap<DeltaChain>();
516 baseByPos = new LongMap<UnresolvedDelta>();
517 deferredCheckBlobs = new BlockList<PackedObjectInfo>();
518
519 receiving.beginTask(JGitText.get().receivingObjects,
520 (int) objectCount);
521 try {
522 for (int done = 0; done < objectCount; done++) {
523 indexOneObject();
524 receiving.update(1);
525 if (receiving.isCancelled())
526 throw new IOException(JGitText.get().downloadCancelled);
527 }
528 readPackFooter();
529 endInput();
530 } finally {
531 receiving.endTask();
532 }
533
534 if (!deferredCheckBlobs.isEmpty())
535 doDeferredCheckBlobs();
536 if (deltaCount > 0) {
537 if (resolving instanceof BatchingProgressMonitor) {
538 ((BatchingProgressMonitor) resolving).setDelayStart(
539 1000,
540 TimeUnit.MILLISECONDS);
541 }
542 resolving.beginTask(JGitText.get().resolvingDeltas, deltaCount);
543 resolveDeltas(resolving);
544 if (entryCount < objectCount) {
545 if (!isAllowThin()) {
546 throw new IOException(MessageFormat.format(
547 JGitText.get().packHasUnresolvedDeltas,
548 Long.valueOf(objectCount - entryCount)));
549 }
550
551 resolveDeltasWithExternalBases(resolving);
552
553 if (entryCount < objectCount) {
554 throw new IOException(MessageFormat.format(
555 JGitText.get().packHasUnresolvedDeltas,
556 Long.valueOf(objectCount - entryCount)));
557 }
558 }
559 resolving.endTask();
560 }
561
562 packDigest = null;
563 baseById = null;
564 baseByPos = null;
565 } finally {
566 try {
567 if (readCurs != null)
568 readCurs.close();
569 } finally {
570 readCurs = null;
571 }
572
573 try {
574 inflater.release();
575 } finally {
576 inflater = null;
577 }
578 }
579 return null;
580 }
581
582 private void resolveDeltas(final ProgressMonitor progress)
583 throws IOException {
584 final int last = entryCount;
585 for (int i = 0; i < last; i++) {
586 resolveDeltas(entries[i], progress);
587 if (progress.isCancelled())
588 throw new IOException(
589 JGitText.get().downloadCancelledDuringIndexing);
590 }
591 }
592
593 private void resolveDeltas(final PackedObjectInfo oe,
594 ProgressMonitor progress) throws IOException {
595 UnresolvedDelta children = firstChildOf(oe);
596 if (children == null)
597 return;
598
599 DeltaVisit visit = new DeltaVisit();
600 visit.nextChild = children;
601
602 ObjectTypeAndSize info = openDatabase(oe, new ObjectTypeAndSize());
603 switch (info.type) {
604 case Constants.OBJ_COMMIT:
605 case Constants.OBJ_TREE:
606 case Constants.OBJ_BLOB:
607 case Constants.OBJ_TAG:
608 visit.data = inflateAndReturn(Source.DATABASE, info.size);
609 visit.id = oe;
610 break;
611 default:
612 throw new IOException(MessageFormat.format(
613 JGitText.get().unknownObjectType,
614 Integer.valueOf(info.type)));
615 }
616
617 if (!checkCRC(oe.getCRC())) {
618 throw new IOException(MessageFormat.format(
619 JGitText.get().corruptionDetectedReReadingAt,
620 Long.valueOf(oe.getOffset())));
621 }
622
623 resolveDeltas(visit.next(), info.type, info, progress);
624 }
625
626 private void resolveDeltas(DeltaVisit visit, final int type,
627 ObjectTypeAndSize info, ProgressMonitor progress)
628 throws IOException {
629 do {
630 progress.update(1);
631 info = openDatabase(visit.delta, info);
632 switch (info.type) {
633 case Constants.OBJ_OFS_DELTA:
634 case Constants.OBJ_REF_DELTA:
635 break;
636
637 default:
638 throw new IOException(MessageFormat.format(
639 JGitText.get().unknownObjectType,
640 Integer.valueOf(info.type)));
641 }
642
643 byte[] delta = inflateAndReturn(Source.DATABASE, info.size);
644 checkIfTooLarge(type, BinaryDelta.getResultSize(delta));
645
646 visit.data = BinaryDelta.apply(visit.parent.data, delta);
647 delta = null;
648
649 if (!checkCRC(visit.delta.crc))
650 throw new IOException(MessageFormat.format(
651 JGitText.get().corruptionDetectedReReadingAt,
652 Long.valueOf(visit.delta.position)));
653
654 objectDigest.update(Constants.encodedTypeString(type));
655 objectDigest.update((byte) ' ');
656 objectDigest.update(Constants.encodeASCII(visit.data.length));
657 objectDigest.update((byte) 0);
658 objectDigest.update(visit.data);
659 tempObjectId.fromRaw(objectDigest.digest(), 0);
660
661 verifySafeObject(tempObjectId, type, visit.data);
662
663 PackedObjectInfo oe;
664 oe = newInfo(tempObjectId, visit.delta, visit.parent.id);
665 oe.setOffset(visit.delta.position);
666 onInflatedObjectData(oe, type, visit.data);
667 addObjectAndTrack(oe);
668 visit.id = oe;
669
670 visit.nextChild = firstChildOf(oe);
671 visit = visit.next();
672 } while (visit != null);
673 }
674
675 private final void checkIfTooLarge(int typeCode, long size)
676 throws IOException {
677 if (0 < maxObjectSizeLimit && maxObjectSizeLimit < size)
678 switch (typeCode) {
679 case Constants.OBJ_COMMIT:
680 case Constants.OBJ_TREE:
681 case Constants.OBJ_BLOB:
682 case Constants.OBJ_TAG:
683 throw new TooLargeObjectInPackException(size, maxObjectSizeLimit);
684
685 case Constants.OBJ_OFS_DELTA:
686 case Constants.OBJ_REF_DELTA:
687 throw new TooLargeObjectInPackException(maxObjectSizeLimit);
688
689 default:
690 throw new IOException(MessageFormat.format(
691 JGitText.get().unknownObjectType,
692 Integer.valueOf(typeCode)));
693 }
694 }
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712 protected ObjectTypeAndSize readObjectHeader(ObjectTypeAndSize info)
713 throws IOException {
714 int hdrPtr = 0;
715 int c = readFrom(Source.DATABASE);
716 hdrBuf[hdrPtr++] = (byte) c;
717
718 info.type = (c >> 4) & 7;
719 long sz = c & 15;
720 int shift = 4;
721 while ((c & 0x80) != 0) {
722 c = readFrom(Source.DATABASE);
723 hdrBuf[hdrPtr++] = (byte) c;
724 sz += ((long) (c & 0x7f)) << shift;
725 shift += 7;
726 }
727 info.size = sz;
728
729 switch (info.type) {
730 case Constants.OBJ_COMMIT:
731 case Constants.OBJ_TREE:
732 case Constants.OBJ_BLOB:
733 case Constants.OBJ_TAG:
734 onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
735 break;
736
737 case Constants.OBJ_OFS_DELTA:
738 c = readFrom(Source.DATABASE);
739 hdrBuf[hdrPtr++] = (byte) c;
740 while ((c & 128) != 0) {
741 c = readFrom(Source.DATABASE);
742 hdrBuf[hdrPtr++] = (byte) c;
743 }
744 onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
745 break;
746
747 case Constants.OBJ_REF_DELTA:
748 System.arraycopy(buf, fill(Source.DATABASE, 20), hdrBuf, hdrPtr, 20);
749 hdrPtr += 20;
750 use(20);
751 onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
752 break;
753
754 default:
755 throw new IOException(MessageFormat.format(
756 JGitText.get().unknownObjectType,
757 Integer.valueOf(info.type)));
758 }
759 return info;
760 }
761
762 private UnresolvedDelta removeBaseById(final AnyObjectId id) {
763 final DeltaChain d = baseById.get(id);
764 return d != null ? d.remove() : null;
765 }
766
767 private static UnresolvedDelta reverse(UnresolvedDelta c) {
768 UnresolvedDelta tail = null;
769 while (c != null) {
770 final UnresolvedDelta n = c.next;
771 c.next = tail;
772 tail = c;
773 c = n;
774 }
775 return tail;
776 }
777
778 private UnresolvedDelta firstChildOf(PackedObjectInfo oe) {
779 UnresolvedDelta a = reverse(removeBaseById(oe));
780 UnresolvedDelta b = reverse(baseByPos.remove(oe.getOffset()));
781
782 if (a == null)
783 return b;
784 if (b == null)
785 return a;
786
787 UnresolvedDelta first = null;
788 UnresolvedDelta last = null;
789 while (a != null || b != null) {
790 UnresolvedDelta curr;
791 if (b == null || (a != null && a.position < b.position)) {
792 curr = a;
793 a = a.next;
794 } else {
795 curr = b;
796 b = b.next;
797 }
798 if (last != null)
799 last.next = curr;
800 else
801 first = curr;
802 last = curr;
803 curr.next = null;
804 }
805 return first;
806 }
807
808 private void resolveDeltasWithExternalBases(final ProgressMonitor progress)
809 throws IOException {
810 growEntries(baseById.size());
811
812 if (needBaseObjectIds)
813 baseObjectIds = new ObjectIdSubclassMap<ObjectId>();
814
815 final List<DeltaChain> missing = new ArrayList<DeltaChain>(64);
816 for (final DeltaChain baseId : baseById) {
817 if (baseId.head == null)
818 continue;
819
820 if (needBaseObjectIds)
821 baseObjectIds.add(baseId);
822
823 final ObjectLoader ldr;
824 try {
825 ldr = readCurs.open(baseId);
826 } catch (MissingObjectException notFound) {
827 missing.add(baseId);
828 continue;
829 }
830
831 final DeltaVisit visit = new DeltaVisit();
832 visit.data = ldr.getCachedBytes(Integer.MAX_VALUE);
833 visit.id = baseId;
834 final int typeCode = ldr.getType();
835 final PackedObjectInfo oe = newInfo(baseId, null, null);
836
837 if (onAppendBase(typeCode, visit.data, oe))
838 entries[entryCount++] = oe;
839
840 visit.nextChild = firstChildOf(oe);
841 resolveDeltas(visit.next(), typeCode,
842 new ObjectTypeAndSize(), progress);
843
844 if (progress.isCancelled())
845 throw new IOException(
846 JGitText.get().downloadCancelledDuringIndexing);
847 }
848
849 for (final DeltaChain base : missing) {
850 if (base.head != null)
851 throw new MissingObjectException(base, "delta base");
852 }
853
854 onEndThinPack();
855 }
856
857 private void growEntries(int extraObjects) {
858 final PackedObjectInfo[] ne;
859
860 ne = new PackedObjectInfo[(int) objectCount + extraObjects];
861 System.arraycopy(entries, 0, ne, 0, entryCount);
862 entries = ne;
863 }
864
865 private void readPackHeader() throws IOException {
866 if (expectDataAfterPackFooter) {
867 if (!in.markSupported())
868 throw new IOException(
869 JGitText.get().inputStreamMustSupportMark);
870 in.mark(buf.length);
871 }
872
873 final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
874 final int p = fill(Source.INPUT, hdrln);
875 for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
876 if (buf[p + k] != Constants.PACK_SIGNATURE[k])
877 throw new IOException(JGitText.get().notAPACKFile);
878
879 final long vers = NB.decodeUInt32(buf, p + 4);
880 if (vers != 2 && vers != 3)
881 throw new IOException(MessageFormat.format(
882 JGitText.get().unsupportedPackVersion, Long.valueOf(vers)));
883 objectCount = NB.decodeUInt32(buf, p + 8);
884 use(hdrln);
885
886 onPackHeader(objectCount);
887 }
888
889 private void readPackFooter() throws IOException {
890 sync();
891 final byte[] actHash = packDigest.digest();
892
893 final int c = fill(Source.INPUT, 20);
894 final byte[] srcHash = new byte[20];
895 System.arraycopy(buf, c, srcHash, 0, 20);
896 use(20);
897
898 if (bAvail != 0 && !expectDataAfterPackFooter)
899 throw new CorruptObjectException(MessageFormat.format(
900 JGitText.get().expectedEOFReceived,
901 "\\x" + Integer.toHexString(buf[bOffset] & 0xff)));
902 if (isCheckEofAfterPackFooter()) {
903 int eof = in.read();
904 if (0 <= eof)
905 throw new CorruptObjectException(MessageFormat.format(
906 JGitText.get().expectedEOFReceived,
907 "\\x" + Integer.toHexString(eof)));
908 } else if (bAvail > 0 && expectDataAfterPackFooter) {
909 in.reset();
910 IO.skipFully(in, bOffset);
911 }
912
913 if (!Arrays.equals(actHash, srcHash))
914 throw new CorruptObjectException(
915 JGitText.get().corruptObjectPackfileChecksumIncorrect);
916
917 onPackFooter(srcHash);
918 }
919
920
921 private void endInput() {
922 in = null;
923 }
924
925
926 private void indexOneObject() throws IOException {
927 final long streamPosition = streamPosition();
928
929 int hdrPtr = 0;
930 int c = readFrom(Source.INPUT);
931 hdrBuf[hdrPtr++] = (byte) c;
932
933 final int typeCode = (c >> 4) & 7;
934 long sz = c & 15;
935 int shift = 4;
936 while ((c & 0x80) != 0) {
937 c = readFrom(Source.INPUT);
938 hdrBuf[hdrPtr++] = (byte) c;
939 sz += ((long) (c & 0x7f)) << shift;
940 shift += 7;
941 }
942
943 checkIfTooLarge(typeCode, sz);
944
945 switch (typeCode) {
946 case Constants.OBJ_COMMIT:
947 case Constants.OBJ_TREE:
948 case Constants.OBJ_BLOB:
949 case Constants.OBJ_TAG:
950 onBeginWholeObject(streamPosition, typeCode, sz);
951 onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
952 whole(streamPosition, typeCode, sz);
953 break;
954
955 case Constants.OBJ_OFS_DELTA: {
956 c = readFrom(Source.INPUT);
957 hdrBuf[hdrPtr++] = (byte) c;
958 long ofs = c & 127;
959 while ((c & 128) != 0) {
960 ofs += 1;
961 c = readFrom(Source.INPUT);
962 hdrBuf[hdrPtr++] = (byte) c;
963 ofs <<= 7;
964 ofs += (c & 127);
965 }
966 final long base = streamPosition - ofs;
967 onBeginOfsDelta(streamPosition, base, sz);
968 onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
969 inflateAndSkip(Source.INPUT, sz);
970 UnresolvedDelta n = onEndDelta();
971 n.position = streamPosition;
972 n.next = baseByPos.put(base, n);
973 deltaCount++;
974 break;
975 }
976
977 case Constants.OBJ_REF_DELTA: {
978 c = fill(Source.INPUT, 20);
979 final ObjectId base = ObjectId.fromRaw(buf, c);
980 System.arraycopy(buf, c, hdrBuf, hdrPtr, 20);
981 hdrPtr += 20;
982 use(20);
983 DeltaChain r = baseById.get(base);
984 if (r == null) {
985 r = new DeltaChain(base);
986 baseById.add(r);
987 }
988 onBeginRefDelta(streamPosition, base, sz);
989 onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
990 inflateAndSkip(Source.INPUT, sz);
991 UnresolvedDelta n = onEndDelta();
992 n.position = streamPosition;
993 r.add(n);
994 deltaCount++;
995 break;
996 }
997
998 default:
999 throw new IOException(
1000 MessageFormat.format(JGitText.get().unknownObjectType,
1001 Integer.valueOf(typeCode)));
1002 }
1003 }
1004
1005 private void whole(final long pos, final int type, final long sz)
1006 throws IOException {
1007 objectDigest.update(Constants.encodedTypeString(type));
1008 objectDigest.update((byte) ' ');
1009 objectDigest.update(Constants.encodeASCII(sz));
1010 objectDigest.update((byte) 0);
1011
1012 final byte[] data;
1013 boolean checkContentLater = false;
1014 if (type == Constants.OBJ_BLOB) {
1015 byte[] readBuffer = buffer();
1016 InputStream inf = inflate(Source.INPUT, sz);
1017 long cnt = 0;
1018 while (cnt < sz) {
1019 int r = inf.read(readBuffer);
1020 if (r <= 0)
1021 break;
1022 objectDigest.update(readBuffer, 0, r);
1023 cnt += r;
1024 }
1025 inf.close();
1026 tempObjectId.fromRaw(objectDigest.digest(), 0);
1027 checkContentLater = isCheckObjectCollisions()
1028 && readCurs.has(tempObjectId);
1029 data = null;
1030
1031 } else {
1032 data = inflateAndReturn(Source.INPUT, sz);
1033 objectDigest.update(data);
1034 tempObjectId.fromRaw(objectDigest.digest(), 0);
1035 verifySafeObject(tempObjectId, type, data);
1036 }
1037
1038 PackedObjectInfo obj = newInfo(tempObjectId, null, null);
1039 obj.setOffset(pos);
1040 onEndWholeObject(obj);
1041 if (data != null)
1042 onInflatedObjectData(obj, type, data);
1043 addObjectAndTrack(obj);
1044 if (checkContentLater)
1045 deferredCheckBlobs.add(obj);
1046 }
1047
1048 private void verifySafeObject(final AnyObjectId id, final int type,
1049 final byte[] data) throws IOException {
1050 if (objCheck != null) {
1051 try {
1052 objCheck.check(id, type, data);
1053 } catch (CorruptObjectException e) {
1054 if (e.getErrorType() != null) {
1055 throw e;
1056 }
1057 throw new CorruptObjectException(MessageFormat.format(
1058 JGitText.get().invalidObject,
1059 Constants.typeString(type),
1060 readCurs.abbreviate(id, 10).name(),
1061 e.getMessage()), e);
1062 }
1063 }
1064
1065 if (isCheckObjectCollisions()) {
1066 try {
1067 final ObjectLoader ldr = readCurs.open(id, type);
1068 final byte[] existingData = ldr.getCachedBytes(data.length);
1069 if (!Arrays.equals(data, existingData)) {
1070 throw new IOException(MessageFormat.format(
1071 JGitText.get().collisionOn, id.name()));
1072 }
1073 } catch (MissingObjectException notLocal) {
1074
1075
1076
1077 }
1078 }
1079 }
1080
1081 private void doDeferredCheckBlobs() throws IOException {
1082 final byte[] readBuffer = buffer();
1083 final byte[] curBuffer = new byte[readBuffer.length];
1084 ObjectTypeAndSize info = new ObjectTypeAndSize();
1085
1086 for (PackedObjectInfo obj : deferredCheckBlobs) {
1087 info = openDatabase(obj, info);
1088
1089 if (info.type != Constants.OBJ_BLOB)
1090 throw new IOException(MessageFormat.format(
1091 JGitText.get().unknownObjectType,
1092 Integer.valueOf(info.type)));
1093
1094 ObjectStream cur = readCurs.open(obj, info.type).openStream();
1095 try {
1096 long sz = info.size;
1097 if (cur.getSize() != sz)
1098 throw new IOException(MessageFormat.format(
1099 JGitText.get().collisionOn, obj.name()));
1100 InputStream pck = inflate(Source.DATABASE, sz);
1101 while (0 < sz) {
1102 int n = (int) Math.min(readBuffer.length, sz);
1103 IO.readFully(cur, curBuffer, 0, n);
1104 IO.readFully(pck, readBuffer, 0, n);
1105 for (int i = 0; i < n; i++) {
1106 if (curBuffer[i] != readBuffer[i])
1107 throw new IOException(MessageFormat.format(JGitText
1108 .get().collisionOn, obj.name()));
1109 }
1110 sz -= n;
1111 }
1112 pck.close();
1113 } finally {
1114 cur.close();
1115 }
1116 }
1117 }
1118
1119
1120 private long streamPosition() {
1121 return bBase + bOffset;
1122 }
1123
1124 private ObjectTypeAndSize openDatabase(PackedObjectInfo obj,
1125 ObjectTypeAndSize info) throws IOException {
1126 bOffset = 0;
1127 bAvail = 0;
1128 return seekDatabase(obj, info);
1129 }
1130
1131 private ObjectTypeAndSize openDatabase(UnresolvedDelta delta,
1132 ObjectTypeAndSize info) throws IOException {
1133 bOffset = 0;
1134 bAvail = 0;
1135 return seekDatabase(delta, info);
1136 }
1137
1138
1139 private int readFrom(final Source src) throws IOException {
1140 if (bAvail == 0)
1141 fill(src, 1);
1142 bAvail--;
1143 return buf[bOffset++] & 0xff;
1144 }
1145
1146
1147 void use(final int cnt) {
1148 bOffset += cnt;
1149 bAvail -= cnt;
1150 }
1151
1152
1153 int fill(final Source src, final int need) throws IOException {
1154 while (bAvail < need) {
1155 int next = bOffset + bAvail;
1156 int free = buf.length - next;
1157 if (free + bAvail < need) {
1158 switch (src) {
1159 case INPUT:
1160 sync();
1161 break;
1162 case DATABASE:
1163 if (bAvail > 0)
1164 System.arraycopy(buf, bOffset, buf, 0, bAvail);
1165 bOffset = 0;
1166 break;
1167 }
1168 next = bAvail;
1169 free = buf.length - next;
1170 }
1171 switch (src) {
1172 case INPUT:
1173 next = in.read(buf, next, free);
1174 break;
1175 case DATABASE:
1176 next = readDatabase(buf, next, free);
1177 break;
1178 }
1179 if (next <= 0)
1180 throw new EOFException(
1181 JGitText.get().packfileIsTruncatedNoParam);
1182 bAvail += next;
1183 }
1184 return bOffset;
1185 }
1186
1187
1188 private void sync() throws IOException {
1189 packDigest.update(buf, 0, bOffset);
1190 onStoreStream(buf, 0, bOffset);
1191 if (expectDataAfterPackFooter) {
1192 if (bAvail > 0) {
1193 in.reset();
1194 IO.skipFully(in, bOffset);
1195 bAvail = 0;
1196 }
1197 in.mark(buf.length);
1198 } else if (bAvail > 0)
1199 System.arraycopy(buf, bOffset, buf, 0, bAvail);
1200 bBase += bOffset;
1201 bOffset = 0;
1202 }
1203
1204
1205 protected byte[] buffer() {
1206 return tempBuffer;
1207 }
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223 protected PackedObjectInfo newInfo(AnyObjectId id, UnresolvedDelta delta,
1224 ObjectId deltaBase) {
1225 PackedObjectInfo oe = new PackedObjectInfo(id);
1226 if (delta != null)
1227 oe.setCRC(delta.crc);
1228 return oe;
1229 }
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252 protected abstract void onStoreStream(byte[] raw, int pos, int len)
1253 throws IOException;
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272 protected abstract void onObjectHeader(Source src, byte[] raw, int pos,
1273 int len) throws IOException;
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295 protected abstract void onObjectData(Source src, byte[] raw, int pos,
1296 int len) throws IOException;
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310 protected abstract void onInflatedObjectData(PackedObjectInfo obj,
1311 int typeCode, byte[] data) throws IOException;
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321 protected abstract void onPackHeader(long objCnt) throws IOException;
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332 protected abstract void onPackFooter(byte[] hash) throws IOException;
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355 protected abstract boolean onAppendBase(int typeCode, byte[] data,
1356 PackedObjectInfo info) throws IOException;
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 protected abstract void onEndThinPack() throws IOException;
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385 protected abstract ObjectTypeAndSize seekDatabase(PackedObjectInfo obj,
1386 ObjectTypeAndSize info) throws IOException;
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403 protected abstract ObjectTypeAndSize seekDatabase(UnresolvedDelta delta,
1404 ObjectTypeAndSize info) throws IOException;
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420 protected abstract int readDatabase(byte[] dst, int pos, int cnt)
1421 throws IOException;
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439 protected abstract boolean checkCRC(int oldCRC);
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456 protected abstract void onBeginWholeObject(long streamPosition, int type,
1457 long inflatedSize) throws IOException;
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467 protected abstract void onEndWholeObject(PackedObjectInfo info)
1468 throws IOException;
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486 protected abstract void onBeginOfsDelta(long deltaStreamPosition,
1487 long baseStreamPosition, long inflatedSize) throws IOException;
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504 protected abstract void onBeginRefDelta(long deltaStreamPosition,
1505 AnyObjectId baseId, long inflatedSize) throws IOException;
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515 protected UnresolvedDelta onEndDelta() throws IOException {
1516 return new UnresolvedDelta();
1517 }
1518
1519
1520 public static class ObjectTypeAndSize {
1521
1522 public int type;
1523
1524
1525 public long size;
1526 }
1527
1528 private void inflateAndSkip(final Source src, final long inflatedSize)
1529 throws IOException {
1530 final InputStream inf = inflate(src, inflatedSize);
1531 IO.skipFully(inf, inflatedSize);
1532 inf.close();
1533 }
1534
1535 private byte[] inflateAndReturn(final Source src, final long inflatedSize)
1536 throws IOException {
1537 final byte[] dst = new byte[(int) inflatedSize];
1538 final InputStream inf = inflate(src, inflatedSize);
1539 IO.readFully(inf, dst, 0, dst.length);
1540 inf.close();
1541 return dst;
1542 }
1543
1544 private InputStream inflate(final Source src, final long inflatedSize)
1545 throws IOException {
1546 inflater.open(src, inflatedSize);
1547 return inflater;
1548 }
1549
1550 private static class DeltaChain extends ObjectIdOwnerMap.Entry {
1551 UnresolvedDelta head;
1552
1553 DeltaChain(final AnyObjectId id) {
1554 super(id);
1555 }
1556
1557 UnresolvedDelta remove() {
1558 final UnresolvedDelta r = head;
1559 if (r != null)
1560 head = null;
1561 return r;
1562 }
1563
1564 void add(final UnresolvedDelta d) {
1565 d.next = head;
1566 head = d;
1567 }
1568 }
1569
1570
1571 public static class UnresolvedDelta {
1572 long position;
1573
1574 int crc;
1575
1576 UnresolvedDelta next;
1577
1578
1579 public long getOffset() {
1580 return position;
1581 }
1582
1583
1584 public int getCRC() {
1585 return crc;
1586 }
1587
1588
1589
1590
1591
1592 public void setCRC(int crc32) {
1593 crc = crc32;
1594 }
1595 }
1596
1597 private static class DeltaVisit {
1598 final UnresolvedDelta delta;
1599
1600 ObjectId id;
1601
1602 byte[] data;
1603
1604 DeltaVisit parent;
1605
1606 UnresolvedDelta nextChild;
1607
1608 DeltaVisit() {
1609 this.delta = null;
1610 }
1611
1612 DeltaVisit(DeltaVisit parent) {
1613 this.parent = parent;
1614 this.delta = parent.nextChild;
1615 parent.nextChild = delta.next;
1616 }
1617
1618 DeltaVisit next() {
1619
1620 if (parent != null && parent.nextChild == null) {
1621 parent.data = null;
1622 parent = parent.parent;
1623 }
1624
1625 if (nextChild != null)
1626 return new DeltaVisit(this);
1627
1628
1629
1630 if (parent != null)
1631 return new DeltaVisit(parent);
1632 return null;
1633 }
1634 }
1635
1636 private void addObjectAndTrack(PackedObjectInfo oe) {
1637 entries[entryCount++] = oe;
1638 if (needNewObjectIds())
1639 newObjectIds.add(oe);
1640 }
1641
1642 private class InflaterStream extends InputStream {
1643 private final Inflater inf;
1644
1645 private final byte[] skipBuffer;
1646
1647 private Source src;
1648
1649 private long expectedSize;
1650
1651 private long actualSize;
1652
1653 private int p;
1654
1655 InflaterStream() {
1656 inf = InflaterCache.get();
1657 skipBuffer = new byte[512];
1658 }
1659
1660 void release() {
1661 inf.reset();
1662 InflaterCache.release(inf);
1663 }
1664
1665 void open(Source source, long inflatedSize) throws IOException {
1666 src = source;
1667 expectedSize = inflatedSize;
1668 actualSize = 0;
1669
1670 p = fill(src, 1);
1671 inf.setInput(buf, p, bAvail);
1672 }
1673
1674 @Override
1675 public long skip(long toSkip) throws IOException {
1676 long n = 0;
1677 while (n < toSkip) {
1678 final int cnt = (int) Math.min(skipBuffer.length, toSkip - n);
1679 final int r = read(skipBuffer, 0, cnt);
1680 if (r <= 0)
1681 break;
1682 n += r;
1683 }
1684 return n;
1685 }
1686
1687 @Override
1688 public int read() throws IOException {
1689 int n = read(skipBuffer, 0, 1);
1690 return n == 1 ? skipBuffer[0] & 0xff : -1;
1691 }
1692
1693 @Override
1694 public int read(byte[] dst, int pos, int cnt) throws IOException {
1695 try {
1696 int n = 0;
1697 while (n < cnt) {
1698 int r = inf.inflate(dst, pos + n, cnt - n);
1699 n += r;
1700 if (inf.finished())
1701 break;
1702 if (inf.needsInput()) {
1703 onObjectData(src, buf, p, bAvail);
1704 use(bAvail);
1705
1706 p = fill(src, 1);
1707 inf.setInput(buf, p, bAvail);
1708 } else if (r == 0) {
1709 throw new CorruptObjectException(MessageFormat.format(
1710 JGitText.get().packfileCorruptionDetected,
1711 JGitText.get().unknownZlibError));
1712 }
1713 }
1714 actualSize += n;
1715 return 0 < n ? n : -1;
1716 } catch (DataFormatException dfe) {
1717 throw new CorruptObjectException(MessageFormat.format(JGitText
1718 .get().packfileCorruptionDetected, dfe.getMessage()));
1719 }
1720 }
1721
1722 @Override
1723 public void close() throws IOException {
1724
1725
1726
1727
1728 if (read(skipBuffer) != -1 || actualSize != expectedSize) {
1729 throw new CorruptObjectException(MessageFormat.format(JGitText
1730 .get().packfileCorruptionDetected,
1731 JGitText.get().wrongDecompressedLength));
1732 }
1733
1734 int used = bAvail - inf.getRemaining();
1735 if (0 < used) {
1736 onObjectData(src, buf, p, used);
1737 use(used);
1738 }
1739
1740 inf.reset();
1741 }
1742 }
1743 }