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
47 package org.eclipse.jgit.transport;
48
49 import static org.junit.Assert.assertEquals;
50 import static org.junit.Assert.assertTrue;
51 import static org.junit.Assert.fail;
52
53 import java.io.ByteArrayInputStream;
54 import java.io.File;
55 import java.io.FileInputStream;
56 import java.io.IOException;
57 import java.io.InputStream;
58 import java.security.MessageDigest;
59 import java.text.MessageFormat;
60 import java.util.zip.Deflater;
61
62 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
63 import org.eclipse.jgit.internal.JGitText;
64 import org.eclipse.jgit.internal.storage.file.ObjectDirectoryPackParser;
65 import org.eclipse.jgit.internal.storage.file.PackFile;
66 import org.eclipse.jgit.junit.JGitTestUtil;
67 import org.eclipse.jgit.junit.RepositoryTestCase;
68 import org.eclipse.jgit.junit.TestRepository;
69 import org.eclipse.jgit.lib.Constants;
70 import org.eclipse.jgit.lib.NullProgressMonitor;
71 import org.eclipse.jgit.lib.ObjectId;
72 import org.eclipse.jgit.lib.ObjectInserter;
73 import org.eclipse.jgit.lib.Repository;
74 import org.eclipse.jgit.revwalk.RevBlob;
75 import org.eclipse.jgit.util.NB;
76 import org.eclipse.jgit.util.TemporaryBuffer;
77 import org.eclipse.jgit.util.io.UnionInputStream;
78 import org.junit.After;
79 import org.junit.Test;
80
81
82
83
84
85
86
87 public class PackParserTest extends RepositoryTestCase {
88
89
90
91
92
93 @Test
94 public void test1() throws IOException {
95 File packFile = JGitTestUtil.getTestResourceFile("pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
96 try (InputStream is = new FileInputStream(packFile)) {
97 ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
98 p.parse(NullProgressMonitor.INSTANCE);
99 PackFile file = p.getPackFile();
100
101 assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
102 assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")));
103 assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259")));
104 assertTrue(file.hasObject(ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3")));
105 assertTrue(file.hasObject(ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
106 assertTrue(file.hasObject(ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327")));
107 assertTrue(file.hasObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035")));
108 assertTrue(file.hasObject(ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
109 }
110 }
111
112
113
114
115
116
117
118 @Test
119 public void test2() throws IOException {
120 File packFile = JGitTestUtil.getTestResourceFile("pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.pack");
121 try (InputStream is = new FileInputStream(packFile)) {
122 ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
123 p.parse(NullProgressMonitor.INSTANCE);
124 PackFile file = p.getPackFile();
125
126 assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9")));
127 assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6")));
128 assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680")));
129 assertTrue(file.hasObject(ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181")));
130 assertTrue(file.hasObject(ObjectId.fromString("1004d0d7ac26fbf63050a234c9b88a46075719d3")));
131 assertTrue(file.hasObject(ObjectId.fromString("10da5895682013006950e7da534b705252b03be6")));
132 assertTrue(file.hasObject(ObjectId.fromString("1203b03dc816ccbb67773f28b3c19318654b0bc8")));
133 assertTrue(file.hasObject(ObjectId.fromString("15fae9e651043de0fd1deef588aa3fbf5a7a41c6")));
134 assertTrue(file.hasObject(ObjectId.fromString("16f9ec009e5568c435f473ba3a1df732d49ce8c3")));
135 assertTrue(file.hasObject(ObjectId.fromString("1fd7d579fb6ae3fe942dc09c2c783443d04cf21e")));
136 assertTrue(file.hasObject(ObjectId.fromString("20a8ade77639491ea0bd667bf95de8abf3a434c8")));
137 assertTrue(file.hasObject(ObjectId.fromString("2675188fd86978d5bc4d7211698b2118ae3bf658")));
138
139 }
140 }
141
142 @Test
143 public void testTinyThinPack() throws Exception {
144 RevBlob a;
145 try (TestRepository d = new TestRepository<Repository>(db)) {
146 a = d.blob("a");
147 }
148
149 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
150
151 packHeader(pack, 1);
152
153 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
154 a.copyRawTo(pack);
155 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
156
157 digest(pack);
158
159 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
160 p.setAllowThin(true);
161 p.parse(NullProgressMonitor.INSTANCE);
162 }
163
164 @Test
165 public void testPackWithDuplicateBlob() throws Exception {
166 final byte[] data = Constants.encode("0123456789abcdefg");
167 try (TestRepository<Repository> d = new TestRepository<>(db)) {
168 assertTrue(db.getObjectDatabase().has(d.blob(data)));
169 }
170
171 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
172 packHeader(pack, 1);
173 pack.write((Constants.OBJ_BLOB) << 4 | 0x80 | 1);
174 pack.write(1);
175 deflate(pack, data);
176 digest(pack);
177
178 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
179 p.setAllowThin(false);
180 p.parse(NullProgressMonitor.INSTANCE);
181 }
182
183 @Test
184 public void testPackWithTrailingGarbage() throws Exception {
185 RevBlob a;
186 try (TestRepository d = new TestRepository<Repository>(db)) {
187 a = d.blob("a");
188 }
189
190 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
191 packHeader(pack, 1);
192 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
193 a.copyRawTo(pack);
194 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
195 digest(pack);
196
197 PackParser p = index(new UnionInputStream(
198 new ByteArrayInputStream(pack.toByteArray()),
199 new ByteArrayInputStream(new byte[] { 0x7e })));
200 p.setAllowThin(true);
201 p.setCheckEofAfterPackFooter(true);
202 try {
203 p.parse(NullProgressMonitor.INSTANCE);
204 fail("Pack with trailing garbage was accepted");
205 } catch (IOException err) {
206 assertEquals(
207 MessageFormat.format(JGitText.get().expectedEOFReceived, "\\x7e"),
208 err.getMessage());
209 }
210 }
211
212 @Test
213 public void testMaxObjectSizeFullBlob() throws Exception {
214 final byte[] data = Constants.encode("0123456789");
215 try (TestRepository d = new TestRepository<Repository>(db)) {
216 d.blob(data);
217 }
218
219 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
220
221 packHeader(pack, 1);
222 pack.write((Constants.OBJ_BLOB) << 4 | 10);
223 deflate(pack, data);
224 digest(pack);
225
226 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
227 p.setMaxObjectSizeLimit(11);
228 p.parse(NullProgressMonitor.INSTANCE);
229
230 p = index(new ByteArrayInputStream(pack.toByteArray()));
231 p.setMaxObjectSizeLimit(10);
232 p.parse(NullProgressMonitor.INSTANCE);
233
234 p = index(new ByteArrayInputStream(pack.toByteArray()));
235 p.setMaxObjectSizeLimit(9);
236 try {
237 p.parse(NullProgressMonitor.INSTANCE);
238 fail("PackParser should have failed");
239 } catch (TooLargeObjectInPackException e) {
240 assertTrue(e.getMessage().contains("10"));
241 assertTrue(e.getMessage().contains("9"));
242 }
243 }
244
245 @Test
246 public void testMaxObjectSizeDeltaBlock() throws Exception {
247 RevBlob a;
248 try (TestRepository d = new TestRepository<Repository>(db)) {
249 a = d.blob("a");
250 }
251
252 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
253
254 packHeader(pack, 1);
255 pack.write((Constants.OBJ_REF_DELTA) << 4 | 14);
256 a.copyRawTo(pack);
257 deflate(pack, new byte[] { 1, 11, 11, 'a', '0', '1', '2', '3', '4',
258 '5', '6', '7', '8', '9' });
259 digest(pack);
260
261 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
262 p.setAllowThin(true);
263 p.setMaxObjectSizeLimit(14);
264 p.parse(NullProgressMonitor.INSTANCE);
265
266 p = index(new ByteArrayInputStream(pack.toByteArray()));
267 p.setAllowThin(true);
268 p.setMaxObjectSizeLimit(13);
269 try {
270 p.parse(NullProgressMonitor.INSTANCE);
271 fail("PackParser should have failed");
272 } catch (TooLargeObjectInPackException e) {
273 assertTrue(e.getMessage().contains("13"));
274 assertTrue(e.getMessage().contains("14"));
275 }
276 }
277
278 @Test
279 public void testMaxObjectSizeDeltaResultSize() throws Exception {
280 RevBlob a;
281 try (TestRepository d = new TestRepository<Repository>(db)) {
282 a = d.blob("0123456789");
283 }
284
285 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
286
287 packHeader(pack, 1);
288 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
289 a.copyRawTo(pack);
290 deflate(pack, new byte[] { 10, 11, 1, 'a' });
291 digest(pack);
292
293 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
294 p.setAllowThin(true);
295 p.setMaxObjectSizeLimit(11);
296 p.parse(NullProgressMonitor.INSTANCE);
297
298 p = index(new ByteArrayInputStream(pack.toByteArray()));
299 p.setAllowThin(true);
300 p.setMaxObjectSizeLimit(10);
301 try {
302 p.parse(NullProgressMonitor.INSTANCE);
303 fail("PackParser should have failed");
304 } catch (TooLargeObjectInPackException e) {
305 assertTrue(e.getMessage().contains("11"));
306 assertTrue(e.getMessage().contains("10"));
307 }
308 }
309
310 @Test
311 public void testNonMarkingInputStream() throws Exception {
312 RevBlob a;
313 try (TestRepository d = new TestRepository<Repository>(db)) {
314 a = d.blob("a");
315 }
316
317 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
318 packHeader(pack, 1);
319 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
320 a.copyRawTo(pack);
321 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
322 digest(pack);
323
324 InputStream in = new ByteArrayInputStream(pack.toByteArray()) {
325 @Override
326 public boolean markSupported() {
327 return false;
328 }
329
330 @Override
331 public void mark(int maxlength) {
332 fail("Mark should not be called");
333 }
334 };
335
336 PackParser p = index(in);
337 p.setAllowThin(true);
338 p.setCheckEofAfterPackFooter(false);
339 p.setExpectDataAfterPackFooter(true);
340
341 try {
342 p.parse(NullProgressMonitor.INSTANCE);
343 fail("PackParser should have failed");
344 } catch (IOException e) {
345 assertEquals(e.getMessage(),
346 JGitText.get().inputStreamMustSupportMark);
347 }
348 }
349
350 @Test
351 public void testDataAfterPackFooterSingleRead() throws Exception {
352 RevBlob a;
353 try (TestRepository d = new TestRepository<Repository>(db)) {
354 a = d.blob("a");
355 }
356
357 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32*1024);
358 packHeader(pack, 1);
359 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
360 a.copyRawTo(pack);
361 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
362 digest(pack);
363
364 byte packData[] = pack.toByteArray();
365 byte streamData[] = new byte[packData.length + 1];
366 System.arraycopy(packData, 0, streamData, 0, packData.length);
367 streamData[packData.length] = 0x7e;
368
369 InputStream in = new ByteArrayInputStream(streamData);
370 PackParser p = index(in);
371 p.setAllowThin(true);
372 p.setCheckEofAfterPackFooter(false);
373 p.setExpectDataAfterPackFooter(true);
374
375 p.parse(NullProgressMonitor.INSTANCE);
376
377 assertEquals(0x7e, in.read());
378 }
379
380 @Test
381 public void testDataAfterPackFooterSplitObjectRead() throws Exception {
382 final byte[] data = Constants.encode("0123456789");
383
384
385 int objects = 900;
386 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
387 packHeader(pack, objects);
388
389 for (int i = 0; i < objects; i++) {
390 pack.write((Constants.OBJ_BLOB) << 4 | 10);
391 deflate(pack, data);
392 }
393 digest(pack);
394
395 byte packData[] = pack.toByteArray();
396 byte streamData[] = new byte[packData.length + 1];
397 System.arraycopy(packData, 0, streamData, 0, packData.length);
398 streamData[packData.length] = 0x7e;
399 InputStream in = new ByteArrayInputStream(streamData);
400 PackParser p = index(in);
401 p.setAllowThin(true);
402 p.setCheckEofAfterPackFooter(false);
403 p.setExpectDataAfterPackFooter(true);
404
405 p.parse(NullProgressMonitor.INSTANCE);
406
407 assertEquals(0x7e, in.read());
408 }
409
410 @Test
411 public void testDataAfterPackFooterSplitHeaderRead() throws Exception {
412 final byte[] data = Constants.encode("a");
413 RevBlob b;
414 try (TestRepository d = new TestRepository<Repository>(db)) {
415 b = d.blob(data);
416 }
417
418 int objects = 248;
419 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
420 packHeader(pack, objects + 1);
421 int offset = 13;
422 StringBuilder sb = new StringBuilder();
423 for (int i = 0; i < offset; i++)
424 sb.append(i);
425 offset = sb.toString().length();
426 int lenByte = (Constants.OBJ_BLOB) << 4 | (offset & 0x0F);
427 offset >>= 4;
428 if (offset > 0)
429 lenByte |= 1 << 7;
430 pack.write(lenByte);
431 while (offset > 0) {
432 lenByte = offset & 0x7F;
433 offset >>= 6;
434 if (offset > 0)
435 lenByte |= 1 << 7;
436 pack.write(lenByte);
437 }
438 deflate(pack, Constants.encode(sb.toString()));
439
440 for (int i = 0; i < objects; i++) {
441
442
443 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
444 b.copyRawTo(pack);
445 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
446 }
447 digest(pack);
448
449 byte packData[] = pack.toByteArray();
450 byte streamData[] = new byte[packData.length + 1];
451 System.arraycopy(packData, 0, streamData, 0, packData.length);
452 streamData[packData.length] = 0x7e;
453 InputStream in = new ByteArrayInputStream(streamData);
454 PackParser p = index(in);
455 p.setAllowThin(true);
456 p.setCheckEofAfterPackFooter(false);
457 p.setExpectDataAfterPackFooter(true);
458
459 p.parse(NullProgressMonitor.INSTANCE);
460
461 assertEquals(0x7e, in.read());
462 }
463
464 private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt)
465 throws IOException {
466 final byte[] hdr = new byte[8];
467 NB.encodeInt32(hdr, 0, 2);
468 NB.encodeInt32(hdr, 4, cnt);
469
470 tinyPack.write(Constants.PACK_SIGNATURE);
471 tinyPack.write(hdr, 0, 8);
472 }
473
474 private static void deflate(TemporaryBuffer.Heap tinyPack,
475 final byte[] content)
476 throws IOException {
477 final Deflater deflater = new Deflater();
478 final byte[] buf = new byte[128];
479 deflater.setInput(content, 0, content.length);
480 deflater.finish();
481 do {
482 final int n = deflater.deflate(buf, 0, buf.length);
483 if (n > 0)
484 tinyPack.write(buf, 0, n);
485 } while (!deflater.finished());
486 }
487
488 private static void digest(TemporaryBuffer.Heap buf) throws IOException {
489 MessageDigest md = Constants.newMessageDigest();
490 md.update(buf.toByteArray());
491 buf.write(md.digest());
492 }
493
494 private ObjectInserter inserter;
495
496 @After
497 public void release() {
498 if (inserter != null) {
499 inserter.close();
500 }
501 }
502
503 private PackParser index(InputStream in) throws IOException {
504 if (inserter == null)
505 inserter = db.newObjectInserter();
506 return inserter.newPackParser(in);
507 }
508 }