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 TestRepository d = new TestRepository<Repository>(db);
145 RevBlob a = d.blob("a");
146
147 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
148
149 packHeader(pack, 1);
150
151 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
152 a.copyRawTo(pack);
153 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
154
155 digest(pack);
156
157 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
158 p.setAllowThin(true);
159 p.parse(NullProgressMonitor.INSTANCE);
160 }
161
162 @Test
163 public void testPackWithDuplicateBlob() throws Exception {
164 final byte[] data = Constants.encode("0123456789abcdefg");
165 TestRepository<Repository> d = new TestRepository<>(db);
166 assertTrue(db.hasObject(d.blob(data)));
167
168 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
169 packHeader(pack, 1);
170 pack.write((Constants.OBJ_BLOB) << 4 | 0x80 | 1);
171 pack.write(1);
172 deflate(pack, data);
173 digest(pack);
174
175 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
176 p.setAllowThin(false);
177 p.parse(NullProgressMonitor.INSTANCE);
178 }
179
180 @Test
181 public void testPackWithTrailingGarbage() throws Exception {
182 TestRepository d = new TestRepository<Repository>(db);
183 RevBlob a = d.blob("a");
184
185 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
186 packHeader(pack, 1);
187 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
188 a.copyRawTo(pack);
189 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
190 digest(pack);
191
192 PackParser p = index(new UnionInputStream(
193 new ByteArrayInputStream(pack.toByteArray()),
194 new ByteArrayInputStream(new byte[] { 0x7e })));
195 p.setAllowThin(true);
196 p.setCheckEofAfterPackFooter(true);
197 try {
198 p.parse(NullProgressMonitor.INSTANCE);
199 fail("Pack with trailing garbage was accepted");
200 } catch (IOException err) {
201 assertEquals(
202 MessageFormat.format(JGitText.get().expectedEOFReceived, "\\x7e"),
203 err.getMessage());
204 }
205 }
206
207 @Test
208 public void testMaxObjectSizeFullBlob() throws Exception {
209 TestRepository d = new TestRepository<Repository>(db);
210 final byte[] data = Constants.encode("0123456789");
211 d.blob(data);
212
213 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
214
215 packHeader(pack, 1);
216 pack.write((Constants.OBJ_BLOB) << 4 | 10);
217 deflate(pack, data);
218 digest(pack);
219
220 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
221 p.setMaxObjectSizeLimit(11);
222 p.parse(NullProgressMonitor.INSTANCE);
223
224 p = index(new ByteArrayInputStream(pack.toByteArray()));
225 p.setMaxObjectSizeLimit(10);
226 p.parse(NullProgressMonitor.INSTANCE);
227
228 p = index(new ByteArrayInputStream(pack.toByteArray()));
229 p.setMaxObjectSizeLimit(9);
230 try {
231 p.parse(NullProgressMonitor.INSTANCE);
232 fail("PackParser should have failed");
233 } catch (TooLargeObjectInPackException e) {
234 assertTrue(e.getMessage().contains("10"));
235 assertTrue(e.getMessage().contains("9"));
236 }
237 }
238
239 @Test
240 public void testMaxObjectSizeDeltaBlock() throws Exception {
241 TestRepository d = new TestRepository<Repository>(db);
242 RevBlob a = d.blob("a");
243
244 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
245
246 packHeader(pack, 1);
247 pack.write((Constants.OBJ_REF_DELTA) << 4 | 14);
248 a.copyRawTo(pack);
249 deflate(pack, new byte[] { 1, 11, 11, 'a', '0', '1', '2', '3', '4',
250 '5', '6', '7', '8', '9' });
251 digest(pack);
252
253 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
254 p.setAllowThin(true);
255 p.setMaxObjectSizeLimit(14);
256 p.parse(NullProgressMonitor.INSTANCE);
257
258 p = index(new ByteArrayInputStream(pack.toByteArray()));
259 p.setAllowThin(true);
260 p.setMaxObjectSizeLimit(13);
261 try {
262 p.parse(NullProgressMonitor.INSTANCE);
263 fail("PackParser should have failed");
264 } catch (TooLargeObjectInPackException e) {
265 assertTrue(e.getMessage().contains("13"));
266 assertTrue(e.getMessage().contains("14"));
267 }
268 }
269
270 @Test
271 public void testMaxObjectSizeDeltaResultSize() throws Exception {
272 TestRepository d = new TestRepository<Repository>(db);
273 RevBlob a = d.blob("0123456789");
274
275 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
276
277 packHeader(pack, 1);
278 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
279 a.copyRawTo(pack);
280 deflate(pack, new byte[] { 10, 11, 1, 'a' });
281 digest(pack);
282
283 PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
284 p.setAllowThin(true);
285 p.setMaxObjectSizeLimit(11);
286 p.parse(NullProgressMonitor.INSTANCE);
287
288 p = index(new ByteArrayInputStream(pack.toByteArray()));
289 p.setAllowThin(true);
290 p.setMaxObjectSizeLimit(10);
291 try {
292 p.parse(NullProgressMonitor.INSTANCE);
293 fail("PackParser should have failed");
294 } catch (TooLargeObjectInPackException e) {
295 assertTrue(e.getMessage().contains("11"));
296 assertTrue(e.getMessage().contains("10"));
297 }
298 }
299
300 @Test
301 public void testNonMarkingInputStream() throws Exception {
302 TestRepository d = new TestRepository<Repository>(db);
303 RevBlob a = d.blob("a");
304
305 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
306 packHeader(pack, 1);
307 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
308 a.copyRawTo(pack);
309 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
310 digest(pack);
311
312 InputStream in = new ByteArrayInputStream(pack.toByteArray()) {
313 @Override
314 public boolean markSupported() {
315 return false;
316 }
317
318 @Override
319 public void mark(int maxlength) {
320 fail("Mark should not be called");
321 }
322 };
323
324 PackParser p = index(in);
325 p.setAllowThin(true);
326 p.setCheckEofAfterPackFooter(false);
327 p.setExpectDataAfterPackFooter(true);
328
329 try {
330 p.parse(NullProgressMonitor.INSTANCE);
331 fail("PackParser should have failed");
332 } catch (IOException e) {
333 assertEquals(e.getMessage(),
334 JGitText.get().inputStreamMustSupportMark);
335 }
336 }
337
338 @Test
339 public void testDataAfterPackFooterSingleRead() throws Exception {
340 TestRepository d = new TestRepository<Repository>(db);
341 RevBlob a = d.blob("a");
342
343 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32*1024);
344 packHeader(pack, 1);
345 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
346 a.copyRawTo(pack);
347 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
348 digest(pack);
349
350 byte packData[] = pack.toByteArray();
351 byte streamData[] = new byte[packData.length + 1];
352 System.arraycopy(packData, 0, streamData, 0, packData.length);
353 streamData[packData.length] = 0x7e;
354
355 InputStream in = new ByteArrayInputStream(streamData);
356 PackParser p = index(in);
357 p.setAllowThin(true);
358 p.setCheckEofAfterPackFooter(false);
359 p.setExpectDataAfterPackFooter(true);
360
361 p.parse(NullProgressMonitor.INSTANCE);
362
363 assertEquals(0x7e, in.read());
364 }
365
366 @Test
367 public void testDataAfterPackFooterSplitObjectRead() throws Exception {
368 final byte[] data = Constants.encode("0123456789");
369
370
371 int objects = 900;
372 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
373 packHeader(pack, objects);
374
375 for (int i = 0; i < objects; i++) {
376 pack.write((Constants.OBJ_BLOB) << 4 | 10);
377 deflate(pack, data);
378 }
379 digest(pack);
380
381 byte packData[] = pack.toByteArray();
382 byte streamData[] = new byte[packData.length + 1];
383 System.arraycopy(packData, 0, streamData, 0, packData.length);
384 streamData[packData.length] = 0x7e;
385 InputStream in = new ByteArrayInputStream(streamData);
386 PackParser p = index(in);
387 p.setAllowThin(true);
388 p.setCheckEofAfterPackFooter(false);
389 p.setExpectDataAfterPackFooter(true);
390
391 p.parse(NullProgressMonitor.INSTANCE);
392
393 assertEquals(0x7e, in.read());
394 }
395
396 @Test
397 public void testDataAfterPackFooterSplitHeaderRead() throws Exception {
398 TestRepository d = new TestRepository<Repository>(db);
399 final byte[] data = Constants.encode("a");
400 RevBlob b = d.blob(data);
401
402 int objects = 248;
403 TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
404 packHeader(pack, objects + 1);
405 int offset = 13;
406 StringBuilder sb = new StringBuilder();
407 for (int i = 0; i < offset; i++)
408 sb.append(i);
409 offset = sb.toString().length();
410 int lenByte = (Constants.OBJ_BLOB) << 4 | (offset & 0x0F);
411 offset >>= 4;
412 if (offset > 0)
413 lenByte |= 1 << 7;
414 pack.write(lenByte);
415 while (offset > 0) {
416 lenByte = offset & 0x7F;
417 offset >>= 6;
418 if (offset > 0)
419 lenByte |= 1 << 7;
420 pack.write(lenByte);
421 }
422 deflate(pack, Constants.encode(sb.toString()));
423
424 for (int i = 0; i < objects; i++) {
425
426
427 pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
428 b.copyRawTo(pack);
429 deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
430 }
431 digest(pack);
432
433 byte packData[] = pack.toByteArray();
434 byte streamData[] = new byte[packData.length + 1];
435 System.arraycopy(packData, 0, streamData, 0, packData.length);
436 streamData[packData.length] = 0x7e;
437 InputStream in = new ByteArrayInputStream(streamData);
438 PackParser p = index(in);
439 p.setAllowThin(true);
440 p.setCheckEofAfterPackFooter(false);
441 p.setExpectDataAfterPackFooter(true);
442
443 p.parse(NullProgressMonitor.INSTANCE);
444
445 assertEquals(0x7e, in.read());
446 }
447
448 private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt)
449 throws IOException {
450 final byte[] hdr = new byte[8];
451 NB.encodeInt32(hdr, 0, 2);
452 NB.encodeInt32(hdr, 4, cnt);
453
454 tinyPack.write(Constants.PACK_SIGNATURE);
455 tinyPack.write(hdr, 0, 8);
456 }
457
458 private static void deflate(TemporaryBuffer.Heap tinyPack,
459 final byte[] content)
460 throws IOException {
461 final Deflater deflater = new Deflater();
462 final byte[] buf = new byte[128];
463 deflater.setInput(content, 0, content.length);
464 deflater.finish();
465 do {
466 final int n = deflater.deflate(buf, 0, buf.length);
467 if (n > 0)
468 tinyPack.write(buf, 0, n);
469 } while (!deflater.finished());
470 }
471
472 private static void digest(TemporaryBuffer.Heap buf) throws IOException {
473 MessageDigest md = Constants.newMessageDigest();
474 md.update(buf.toByteArray());
475 buf.write(md.digest());
476 }
477
478 private ObjectInserter inserter;
479
480 @After
481 public void release() {
482 if (inserter != null) {
483 inserter.close();
484 }
485 }
486
487 private PackParser index(InputStream in) throws IOException {
488 if (inserter == null)
489 inserter = db.newObjectInserter();
490 return inserter.newPackParser(in);
491 }
492 }