You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PackParserTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * Copyright (C) 2009, Google Inc.
  3. * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
  4. * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  6. * and other copyright owners as documented in the project's IP log.
  7. *
  8. * This program and the accompanying materials are made available
  9. * under the terms of the Eclipse Distribution License v1.0 which
  10. * accompanies this distribution, is reproduced below, and is
  11. * available at http://www.eclipse.org/org/documents/edl-v10.php
  12. *
  13. * All rights reserved.
  14. *
  15. * Redistribution and use in source and binary forms, with or
  16. * without modification, are permitted provided that the following
  17. * conditions are met:
  18. *
  19. * - Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. *
  22. * - Redistributions in binary form must reproduce the above
  23. * copyright notice, this list of conditions and the following
  24. * disclaimer in the documentation and/or other materials provided
  25. * with the distribution.
  26. *
  27. * - Neither the name of the Eclipse Foundation, Inc. nor the
  28. * names of its contributors may be used to endorse or promote
  29. * products derived from this software without specific prior
  30. * written permission.
  31. *
  32. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  33. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  34. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  36. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  37. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  39. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  41. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  42. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  43. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  44. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. */
  46. package org.eclipse.jgit.transport;
  47. import static org.junit.Assert.assertEquals;
  48. import static org.junit.Assert.assertTrue;
  49. import static org.junit.Assert.fail;
  50. import java.io.ByteArrayInputStream;
  51. import java.io.File;
  52. import java.io.FileInputStream;
  53. import java.io.IOException;
  54. import java.io.InputStream;
  55. import java.security.MessageDigest;
  56. import java.text.MessageFormat;
  57. import java.util.zip.Deflater;
  58. import org.eclipse.jgit.errors.TooLargeObjectInPackException;
  59. import org.eclipse.jgit.internal.JGitText;
  60. import org.eclipse.jgit.internal.storage.file.ObjectDirectoryPackParser;
  61. import org.eclipse.jgit.internal.storage.file.PackFile;
  62. import org.eclipse.jgit.junit.JGitTestUtil;
  63. import org.eclipse.jgit.junit.RepositoryTestCase;
  64. import org.eclipse.jgit.junit.TestRepository;
  65. import org.eclipse.jgit.lib.Constants;
  66. import org.eclipse.jgit.lib.NullProgressMonitor;
  67. import org.eclipse.jgit.lib.ObjectId;
  68. import org.eclipse.jgit.lib.ObjectInserter;
  69. import org.eclipse.jgit.lib.Repository;
  70. import org.eclipse.jgit.revwalk.RevBlob;
  71. import org.eclipse.jgit.util.NB;
  72. import org.eclipse.jgit.util.TemporaryBuffer;
  73. import org.eclipse.jgit.util.io.UnionInputStream;
  74. import org.junit.After;
  75. import org.junit.Test;
  76. /**
  77. * Test indexing of git packs. A pack is read from a stream, copied
  78. * to a new pack and an index is created. Then the packs are tested
  79. * to make sure they contain the expected objects (well we don't test
  80. * for all of them unless the packs are very small).
  81. */
  82. public class PackParserTest extends RepositoryTestCase {
  83. /**
  84. * Test indexing one of the test packs in the egit repo. It has deltas.
  85. *
  86. * @throws IOException
  87. */
  88. @Test
  89. public void test1() throws IOException {
  90. File packFile = JGitTestUtil.getTestResourceFile("pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
  91. try (InputStream is = new FileInputStream(packFile)) {
  92. ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
  93. p.parse(NullProgressMonitor.INSTANCE);
  94. PackFile file = p.getPackFile();
  95. assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
  96. assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")));
  97. assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259")));
  98. assertTrue(file.hasObject(ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3")));
  99. assertTrue(file.hasObject(ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
  100. assertTrue(file.hasObject(ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327")));
  101. assertTrue(file.hasObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035")));
  102. assertTrue(file.hasObject(ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
  103. }
  104. }
  105. /**
  106. * This is just another pack. It so happens that we have two convenient pack to
  107. * test with in the repository.
  108. *
  109. * @throws IOException
  110. */
  111. @Test
  112. public void test2() throws IOException {
  113. File packFile = JGitTestUtil.getTestResourceFile("pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.pack");
  114. try (InputStream is = new FileInputStream(packFile)) {
  115. ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(is);
  116. p.parse(NullProgressMonitor.INSTANCE);
  117. PackFile file = p.getPackFile();
  118. assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9")));
  119. assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6")));
  120. assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680")));
  121. assertTrue(file.hasObject(ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181")));
  122. assertTrue(file.hasObject(ObjectId.fromString("1004d0d7ac26fbf63050a234c9b88a46075719d3")));
  123. assertTrue(file.hasObject(ObjectId.fromString("10da5895682013006950e7da534b705252b03be6")));
  124. assertTrue(file.hasObject(ObjectId.fromString("1203b03dc816ccbb67773f28b3c19318654b0bc8")));
  125. assertTrue(file.hasObject(ObjectId.fromString("15fae9e651043de0fd1deef588aa3fbf5a7a41c6")));
  126. assertTrue(file.hasObject(ObjectId.fromString("16f9ec009e5568c435f473ba3a1df732d49ce8c3")));
  127. assertTrue(file.hasObject(ObjectId.fromString("1fd7d579fb6ae3fe942dc09c2c783443d04cf21e")));
  128. assertTrue(file.hasObject(ObjectId.fromString("20a8ade77639491ea0bd667bf95de8abf3a434c8")));
  129. assertTrue(file.hasObject(ObjectId.fromString("2675188fd86978d5bc4d7211698b2118ae3bf658")));
  130. // and lots more...
  131. }
  132. }
  133. @Test
  134. public void testTinyThinPack() throws Exception {
  135. TestRepository d = new TestRepository<Repository>(db);
  136. RevBlob a = d.blob("a");
  137. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  138. packHeader(pack, 1);
  139. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  140. a.copyRawTo(pack);
  141. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  142. digest(pack);
  143. PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
  144. p.setAllowThin(true);
  145. p.parse(NullProgressMonitor.INSTANCE);
  146. }
  147. @Test
  148. public void testPackWithDuplicateBlob() throws Exception {
  149. final byte[] data = Constants.encode("0123456789abcdefg");
  150. TestRepository<Repository> d = new TestRepository<>(db);
  151. assertTrue(db.hasObject(d.blob(data)));
  152. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  153. packHeader(pack, 1);
  154. pack.write((Constants.OBJ_BLOB) << 4 | 0x80 | 1);
  155. pack.write(1);
  156. deflate(pack, data);
  157. digest(pack);
  158. PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
  159. p.setAllowThin(false);
  160. p.parse(NullProgressMonitor.INSTANCE);
  161. }
  162. @Test
  163. public void testPackWithTrailingGarbage() throws Exception {
  164. TestRepository d = new TestRepository<Repository>(db);
  165. RevBlob a = d.blob("a");
  166. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  167. packHeader(pack, 1);
  168. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  169. a.copyRawTo(pack);
  170. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  171. digest(pack);
  172. PackParser p = index(new UnionInputStream(
  173. new ByteArrayInputStream(pack.toByteArray()),
  174. new ByteArrayInputStream(new byte[] { 0x7e })));
  175. p.setAllowThin(true);
  176. p.setCheckEofAfterPackFooter(true);
  177. try {
  178. p.parse(NullProgressMonitor.INSTANCE);
  179. fail("Pack with trailing garbage was accepted");
  180. } catch (IOException err) {
  181. assertEquals(
  182. MessageFormat.format(JGitText.get().expectedEOFReceived, "\\x7e"),
  183. err.getMessage());
  184. }
  185. }
  186. @Test
  187. public void testMaxObjectSizeFullBlob() throws Exception {
  188. TestRepository d = new TestRepository<Repository>(db);
  189. final byte[] data = Constants.encode("0123456789");
  190. d.blob(data);
  191. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  192. packHeader(pack, 1);
  193. pack.write((Constants.OBJ_BLOB) << 4 | 10);
  194. deflate(pack, data);
  195. digest(pack);
  196. PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
  197. p.setMaxObjectSizeLimit(11);
  198. p.parse(NullProgressMonitor.INSTANCE);
  199. p = index(new ByteArrayInputStream(pack.toByteArray()));
  200. p.setMaxObjectSizeLimit(10);
  201. p.parse(NullProgressMonitor.INSTANCE);
  202. p = index(new ByteArrayInputStream(pack.toByteArray()));
  203. p.setMaxObjectSizeLimit(9);
  204. try {
  205. p.parse(NullProgressMonitor.INSTANCE);
  206. fail("PackParser should have failed");
  207. } catch (TooLargeObjectInPackException e) {
  208. assertTrue(e.getMessage().contains("10")); // obj size
  209. assertTrue(e.getMessage().contains("9")); // max obj size
  210. }
  211. }
  212. @Test
  213. public void testMaxObjectSizeDeltaBlock() throws Exception {
  214. TestRepository d = new TestRepository<Repository>(db);
  215. RevBlob a = d.blob("a");
  216. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  217. packHeader(pack, 1);
  218. pack.write((Constants.OBJ_REF_DELTA) << 4 | 14);
  219. a.copyRawTo(pack);
  220. deflate(pack, new byte[] { 1, 11, 11, 'a', '0', '1', '2', '3', '4',
  221. '5', '6', '7', '8', '9' });
  222. digest(pack);
  223. PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
  224. p.setAllowThin(true);
  225. p.setMaxObjectSizeLimit(14);
  226. p.parse(NullProgressMonitor.INSTANCE);
  227. p = index(new ByteArrayInputStream(pack.toByteArray()));
  228. p.setAllowThin(true);
  229. p.setMaxObjectSizeLimit(13);
  230. try {
  231. p.parse(NullProgressMonitor.INSTANCE);
  232. fail("PackParser should have failed");
  233. } catch (TooLargeObjectInPackException e) {
  234. assertTrue(e.getMessage().contains("13")); // max obj size
  235. assertTrue(e.getMessage().contains("14")); // delta size
  236. }
  237. }
  238. @Test
  239. public void testMaxObjectSizeDeltaResultSize() throws Exception {
  240. TestRepository d = new TestRepository<Repository>(db);
  241. RevBlob a = d.blob("0123456789");
  242. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  243. packHeader(pack, 1);
  244. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  245. a.copyRawTo(pack);
  246. deflate(pack, new byte[] { 10, 11, 1, 'a' });
  247. digest(pack);
  248. PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
  249. p.setAllowThin(true);
  250. p.setMaxObjectSizeLimit(11);
  251. p.parse(NullProgressMonitor.INSTANCE);
  252. p = index(new ByteArrayInputStream(pack.toByteArray()));
  253. p.setAllowThin(true);
  254. p.setMaxObjectSizeLimit(10);
  255. try {
  256. p.parse(NullProgressMonitor.INSTANCE);
  257. fail("PackParser should have failed");
  258. } catch (TooLargeObjectInPackException e) {
  259. assertTrue(e.getMessage().contains("11")); // result obj size
  260. assertTrue(e.getMessage().contains("10")); // max obj size
  261. }
  262. }
  263. @Test
  264. public void testNonMarkingInputStream() throws Exception {
  265. TestRepository d = new TestRepository<Repository>(db);
  266. RevBlob a = d.blob("a");
  267. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  268. packHeader(pack, 1);
  269. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  270. a.copyRawTo(pack);
  271. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  272. digest(pack);
  273. InputStream in = new ByteArrayInputStream(pack.toByteArray()) {
  274. @Override
  275. public boolean markSupported() {
  276. return false;
  277. }
  278. @Override
  279. public void mark(int maxlength) {
  280. fail("Mark should not be called");
  281. }
  282. };
  283. PackParser p = index(in);
  284. p.setAllowThin(true);
  285. p.setCheckEofAfterPackFooter(false);
  286. p.setExpectDataAfterPackFooter(true);
  287. try {
  288. p.parse(NullProgressMonitor.INSTANCE);
  289. fail("PackParser should have failed");
  290. } catch (IOException e) {
  291. assertEquals(e.getMessage(),
  292. JGitText.get().inputStreamMustSupportMark);
  293. }
  294. }
  295. @Test
  296. public void testDataAfterPackFooterSingleRead() throws Exception {
  297. TestRepository d = new TestRepository<Repository>(db);
  298. RevBlob a = d.blob("a");
  299. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32*1024);
  300. packHeader(pack, 1);
  301. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  302. a.copyRawTo(pack);
  303. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  304. digest(pack);
  305. byte packData[] = pack.toByteArray();
  306. byte streamData[] = new byte[packData.length + 1];
  307. System.arraycopy(packData, 0, streamData, 0, packData.length);
  308. streamData[packData.length] = 0x7e;
  309. InputStream in = new ByteArrayInputStream(streamData);
  310. PackParser p = index(in);
  311. p.setAllowThin(true);
  312. p.setCheckEofAfterPackFooter(false);
  313. p.setExpectDataAfterPackFooter(true);
  314. p.parse(NullProgressMonitor.INSTANCE);
  315. assertEquals(0x7e, in.read());
  316. }
  317. @Test
  318. public void testDataAfterPackFooterSplitObjectRead() throws Exception {
  319. final byte[] data = Constants.encode("0123456789");
  320. // Build a pack ~17k
  321. int objects = 900;
  322. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
  323. packHeader(pack, objects);
  324. for (int i = 0; i < objects; i++) {
  325. pack.write((Constants.OBJ_BLOB) << 4 | 10);
  326. deflate(pack, data);
  327. }
  328. digest(pack);
  329. byte packData[] = pack.toByteArray();
  330. byte streamData[] = new byte[packData.length + 1];
  331. System.arraycopy(packData, 0, streamData, 0, packData.length);
  332. streamData[packData.length] = 0x7e;
  333. InputStream in = new ByteArrayInputStream(streamData);
  334. PackParser p = index(in);
  335. p.setAllowThin(true);
  336. p.setCheckEofAfterPackFooter(false);
  337. p.setExpectDataAfterPackFooter(true);
  338. p.parse(NullProgressMonitor.INSTANCE);
  339. assertEquals(0x7e, in.read());
  340. }
  341. @Test
  342. public void testDataAfterPackFooterSplitHeaderRead() throws Exception {
  343. TestRepository d = new TestRepository<Repository>(db);
  344. final byte[] data = Constants.encode("a");
  345. RevBlob b = d.blob(data);
  346. int objects = 248;
  347. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
  348. packHeader(pack, objects + 1);
  349. int offset = 13;
  350. StringBuilder sb = new StringBuilder();
  351. for (int i = 0; i < offset; i++)
  352. sb.append(i);
  353. offset = sb.toString().length();
  354. int lenByte = (Constants.OBJ_BLOB) << 4 | (offset & 0x0F);
  355. offset >>= 4;
  356. if (offset > 0)
  357. lenByte |= 1 << 7;
  358. pack.write(lenByte);
  359. while (offset > 0) {
  360. lenByte = offset & 0x7F;
  361. offset >>= 6;
  362. if (offset > 0)
  363. lenByte |= 1 << 7;
  364. pack.write(lenByte);
  365. }
  366. deflate(pack, Constants.encode(sb.toString()));
  367. for (int i = 0; i < objects; i++) {
  368. // The last pack header written falls across the 8192 byte boundary
  369. // between [8189:8210]
  370. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  371. b.copyRawTo(pack);
  372. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  373. }
  374. digest(pack);
  375. byte packData[] = pack.toByteArray();
  376. byte streamData[] = new byte[packData.length + 1];
  377. System.arraycopy(packData, 0, streamData, 0, packData.length);
  378. streamData[packData.length] = 0x7e;
  379. InputStream in = new ByteArrayInputStream(streamData);
  380. PackParser p = index(in);
  381. p.setAllowThin(true);
  382. p.setCheckEofAfterPackFooter(false);
  383. p.setExpectDataAfterPackFooter(true);
  384. p.parse(NullProgressMonitor.INSTANCE);
  385. assertEquals(0x7e, in.read());
  386. }
  387. private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt)
  388. throws IOException {
  389. final byte[] hdr = new byte[8];
  390. NB.encodeInt32(hdr, 0, 2);
  391. NB.encodeInt32(hdr, 4, cnt);
  392. tinyPack.write(Constants.PACK_SIGNATURE);
  393. tinyPack.write(hdr, 0, 8);
  394. }
  395. private static void deflate(TemporaryBuffer.Heap tinyPack,
  396. final byte[] content)
  397. throws IOException {
  398. final Deflater deflater = new Deflater();
  399. final byte[] buf = new byte[128];
  400. deflater.setInput(content, 0, content.length);
  401. deflater.finish();
  402. do {
  403. final int n = deflater.deflate(buf, 0, buf.length);
  404. if (n > 0)
  405. tinyPack.write(buf, 0, n);
  406. } while (!deflater.finished());
  407. }
  408. private static void digest(TemporaryBuffer.Heap buf) throws IOException {
  409. MessageDigest md = Constants.newMessageDigest();
  410. md.update(buf.toByteArray());
  411. buf.write(md.digest());
  412. }
  413. private ObjectInserter inserter;
  414. @After
  415. public void release() {
  416. if (inserter != null) {
  417. inserter.close();
  418. }
  419. }
  420. private PackParser index(InputStream in) throws IOException {
  421. if (inserter == null)
  422. inserter = db.newObjectInserter();
  423. return inserter.newPackParser(in);
  424. }
  425. }