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