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