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