Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

UnpackedObjectTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /*
  2. * Copyright (C) 2010, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.internal.storage.file;
  11. import static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertFalse;
  13. import static org.junit.Assert.assertNotNull;
  14. import static org.junit.Assert.assertTrue;
  15. import static org.junit.Assert.fail;
  16. import java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.text.MessageFormat;
  24. import java.util.Arrays;
  25. import java.util.zip.DeflaterOutputStream;
  26. import org.eclipse.jgit.errors.CorruptObjectException;
  27. import org.eclipse.jgit.errors.LargeObjectException;
  28. import org.eclipse.jgit.internal.JGitText;
  29. import org.eclipse.jgit.junit.JGitTestUtil;
  30. import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
  31. import org.eclipse.jgit.junit.TestRng;
  32. import org.eclipse.jgit.lib.Constants;
  33. import org.eclipse.jgit.lib.ObjectId;
  34. import org.eclipse.jgit.lib.ObjectInserter;
  35. import org.eclipse.jgit.lib.ObjectLoader;
  36. import org.eclipse.jgit.lib.ObjectStream;
  37. import org.eclipse.jgit.storage.file.WindowCacheConfig;
  38. import org.eclipse.jgit.util.FileUtils;
  39. import org.eclipse.jgit.util.IO;
  40. import org.junit.After;
  41. import org.junit.Before;
  42. import org.junit.Test;
  43. public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
  44. private int streamThreshold = 16 * 1024;
  45. private TestRng rng;
  46. private FileRepository repo;
  47. private WindowCursor wc;
  48. private TestRng getRng() {
  49. if (rng == null)
  50. rng = new TestRng(JGitTestUtil.getName());
  51. return rng;
  52. }
  53. @Override
  54. @Before
  55. public void setUp() throws Exception {
  56. super.setUp();
  57. WindowCacheConfig cfg = new WindowCacheConfig();
  58. cfg.setStreamFileThreshold(streamThreshold);
  59. cfg.install();
  60. repo = createBareRepository();
  61. wc = (WindowCursor) repo.newObjectReader();
  62. }
  63. @Override
  64. @After
  65. public void tearDown() throws Exception {
  66. if (wc != null)
  67. wc.close();
  68. new WindowCacheConfig().install();
  69. super.tearDown();
  70. }
  71. @Test
  72. public void testStandardFormat_SmallObject() throws Exception {
  73. final int type = Constants.OBJ_BLOB;
  74. byte[] data = getRng().nextBytes(300);
  75. byte[] gz = compressStandardFormat(type, data);
  76. ObjectId id = ObjectId.zeroId();
  77. ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
  78. path(id), id, wc);
  79. assertNotNull("created loader", ol);
  80. assertEquals(type, ol.getType());
  81. assertEquals(data.length, ol.getSize());
  82. assertFalse("is not large", ol.isLarge());
  83. assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
  84. try (ObjectStream in = ol.openStream()) {
  85. assertNotNull("have stream", in);
  86. assertEquals(type, in.getType());
  87. assertEquals(data.length, in.getSize());
  88. byte[] data2 = new byte[data.length];
  89. IO.readFully(in, data2, 0, data.length);
  90. assertTrue("same content", Arrays.equals(data2, data));
  91. assertEquals("stream at EOF", -1, in.read());
  92. }
  93. }
  94. @Test
  95. public void testStandardFormat_LargeObject() throws Exception {
  96. final int type = Constants.OBJ_BLOB;
  97. byte[] data = getRng().nextBytes(streamThreshold + 5);
  98. ObjectId id = getId(type, data);
  99. write(id, compressStandardFormat(type, data));
  100. ObjectLoader ol;
  101. {
  102. try (FileInputStream fs = new FileInputStream(path(id))) {
  103. ol = UnpackedObject.open(fs, path(id), id, wc);
  104. }
  105. }
  106. assertNotNull("created loader", ol);
  107. assertEquals(type, ol.getType());
  108. assertEquals(data.length, ol.getSize());
  109. assertTrue("is large", ol.isLarge());
  110. try {
  111. ol.getCachedBytes();
  112. fail("Should have thrown LargeObjectException");
  113. } catch (LargeObjectException tooBig) {
  114. assertEquals(MessageFormat.format(
  115. JGitText.get().largeObjectException, id.name()), tooBig
  116. .getMessage());
  117. }
  118. try (ObjectStream in = ol.openStream()) {
  119. assertNotNull("have stream", in);
  120. assertEquals(type, in.getType());
  121. assertEquals(data.length, in.getSize());
  122. byte[] data2 = new byte[data.length];
  123. IO.readFully(in, data2, 0, data.length);
  124. assertTrue("same content", Arrays.equals(data2, data));
  125. assertEquals("stream at EOF", -1, in.read());
  126. }
  127. }
  128. @Test
  129. public void testStandardFormat_NegativeSize() throws Exception {
  130. ObjectId id = ObjectId.zeroId();
  131. byte[] data = getRng().nextBytes(300);
  132. try {
  133. byte[] gz = compressStandardFormat("blob", "-1", data);
  134. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  135. fail("Did not throw CorruptObjectException");
  136. } catch (CorruptObjectException coe) {
  137. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  138. id.name(), JGitText.get().corruptObjectNegativeSize), coe
  139. .getMessage());
  140. }
  141. }
  142. @Test
  143. public void testStandardFormat_InvalidType() throws Exception {
  144. ObjectId id = ObjectId.zeroId();
  145. byte[] data = getRng().nextBytes(300);
  146. try {
  147. byte[] gz = compressStandardFormat("not.a.type", "1", data);
  148. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  149. fail("Did not throw CorruptObjectException");
  150. } catch (CorruptObjectException coe) {
  151. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  152. id.name(), JGitText.get().corruptObjectInvalidType), coe
  153. .getMessage());
  154. }
  155. }
  156. @Test
  157. public void testStandardFormat_NoHeader() throws Exception {
  158. ObjectId id = ObjectId.zeroId();
  159. byte[] data = {};
  160. try {
  161. byte[] gz = compressStandardFormat("", "", data);
  162. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  163. fail("Did not throw CorruptObjectException");
  164. } catch (CorruptObjectException coe) {
  165. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  166. id.name(), JGitText.get().corruptObjectNoHeader), coe
  167. .getMessage());
  168. }
  169. }
  170. @Test
  171. public void testStandardFormat_GarbageAfterSize() throws Exception {
  172. ObjectId id = ObjectId.zeroId();
  173. byte[] data = getRng().nextBytes(300);
  174. try {
  175. byte[] gz = compressStandardFormat("blob", "1foo", data);
  176. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  177. fail("Did not throw CorruptObjectException");
  178. } catch (CorruptObjectException coe) {
  179. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  180. id.name(), JGitText.get().corruptObjectGarbageAfterSize),
  181. coe.getMessage());
  182. }
  183. }
  184. @Test
  185. public void testStandardFormat_SmallObject_CorruptZLibStream()
  186. throws Exception {
  187. ObjectId id = ObjectId.zeroId();
  188. byte[] data = getRng().nextBytes(300);
  189. try {
  190. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  191. for (int i = 5; i < gz.length; i++)
  192. gz[i] = 0;
  193. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  194. fail("Did not throw CorruptObjectException");
  195. } catch (CorruptObjectException coe) {
  196. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  197. id.name(), JGitText.get().corruptObjectBadStream), coe
  198. .getMessage());
  199. }
  200. }
  201. @Test
  202. public void testStandardFormat_SmallObject_TruncatedZLibStream()
  203. throws Exception {
  204. ObjectId id = ObjectId.zeroId();
  205. byte[] data = getRng().nextBytes(300);
  206. try {
  207. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  208. byte[] tr = new byte[gz.length - 1];
  209. System.arraycopy(gz, 0, tr, 0, tr.length);
  210. UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
  211. fail("Did not throw CorruptObjectException");
  212. } catch (CorruptObjectException coe) {
  213. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  214. id.name(), JGitText.get().corruptObjectBadStream), coe
  215. .getMessage());
  216. }
  217. }
  218. @Test
  219. public void testStandardFormat_SmallObject_TrailingGarbage()
  220. throws Exception {
  221. ObjectId id = ObjectId.zeroId();
  222. byte[] data = getRng().nextBytes(300);
  223. try {
  224. byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
  225. byte[] tr = new byte[gz.length + 1];
  226. System.arraycopy(gz, 0, tr, 0, gz.length);
  227. UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
  228. fail("Did not throw CorruptObjectException");
  229. } catch (CorruptObjectException coe) {
  230. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  231. id.name(), JGitText.get().corruptObjectBadStream), coe
  232. .getMessage());
  233. }
  234. }
  235. @Test
  236. public void testStandardFormat_LargeObject_CorruptZLibStream()
  237. throws Exception {
  238. final int type = Constants.OBJ_BLOB;
  239. byte[] data = getRng().nextBytes(streamThreshold + 5);
  240. ObjectId id = getId(type, data);
  241. byte[] gz = compressStandardFormat(type, data);
  242. gz[gz.length - 1] = 0;
  243. gz[gz.length - 2] = 0;
  244. write(id, gz);
  245. ObjectLoader ol;
  246. try (FileInputStream fs = new FileInputStream(path(id))) {
  247. ol = UnpackedObject.open(fs, path(id), id, wc);
  248. }
  249. byte[] tmp = new byte[data.length];
  250. try (InputStream in = ol.openStream()) {
  251. IO.readFully(in, tmp, 0, tmp.length);
  252. fail("Did not throw CorruptObjectException");
  253. } catch (CorruptObjectException coe) {
  254. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  255. id.name(), JGitText.get().corruptObjectBadStream), coe
  256. .getMessage());
  257. }
  258. }
  259. @Test
  260. public void testStandardFormat_LargeObject_TruncatedZLibStream()
  261. throws Exception {
  262. final int type = Constants.OBJ_BLOB;
  263. byte[] data = getRng().nextBytes(streamThreshold + 5);
  264. ObjectId id = getId(type, data);
  265. byte[] gz = compressStandardFormat(type, data);
  266. byte[] tr = new byte[gz.length - 1];
  267. System.arraycopy(gz, 0, tr, 0, tr.length);
  268. write(id, tr);
  269. ObjectLoader ol;
  270. try (FileInputStream fs = new FileInputStream(path(id))) {
  271. ol = UnpackedObject.open(fs, path(id), id, wc);
  272. }
  273. byte[] tmp = new byte[data.length];
  274. @SuppressWarnings("resource") // We are testing that the close() method throws
  275. InputStream in = ol.openStream();
  276. IO.readFully(in, tmp, 0, tmp.length);
  277. try {
  278. in.close();
  279. fail("close did not throw CorruptObjectException");
  280. } catch (CorruptObjectException coe) {
  281. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  282. id.name(), JGitText.get().corruptObjectBadStream), coe
  283. .getMessage());
  284. }
  285. }
  286. @Test
  287. public void testStandardFormat_LargeObject_TrailingGarbage()
  288. throws Exception {
  289. final int type = Constants.OBJ_BLOB;
  290. byte[] data = getRng().nextBytes(streamThreshold + 5);
  291. ObjectId id = getId(type, data);
  292. byte[] gz = compressStandardFormat(type, data);
  293. byte[] tr = new byte[gz.length + 1];
  294. System.arraycopy(gz, 0, tr, 0, gz.length);
  295. write(id, tr);
  296. ObjectLoader ol;
  297. try (FileInputStream fs = new FileInputStream(path(id))) {
  298. ol = UnpackedObject.open(fs, path(id), id, wc);
  299. }
  300. byte[] tmp = new byte[data.length];
  301. @SuppressWarnings("resource") // We are testing that the close() method throws
  302. InputStream in = ol.openStream();
  303. IO.readFully(in, tmp, 0, tmp.length);
  304. try {
  305. in.close();
  306. fail("close did not throw CorruptObjectException");
  307. } catch (CorruptObjectException coe) {
  308. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  309. id.name(), JGitText.get().corruptObjectBadStream), coe
  310. .getMessage());
  311. }
  312. }
  313. @Test
  314. public void testPackFormat_SmallObject() throws Exception {
  315. final int type = Constants.OBJ_BLOB;
  316. byte[] data = getRng().nextBytes(300);
  317. byte[] gz = compressPackFormat(type, data);
  318. ObjectId id = ObjectId.zeroId();
  319. ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
  320. path(id), id, wc);
  321. assertNotNull("created loader", ol);
  322. assertEquals(type, ol.getType());
  323. assertEquals(data.length, ol.getSize());
  324. assertFalse("is not large", ol.isLarge());
  325. assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
  326. try (ObjectStream in = ol.openStream()) {
  327. assertNotNull("have stream", in);
  328. assertEquals(type, in.getType());
  329. assertEquals(data.length, in.getSize());
  330. byte[] data2 = new byte[data.length];
  331. IO.readFully(in, data2, 0, data.length);
  332. assertTrue("same content",
  333. Arrays.equals(data, ol.getCachedBytes()));
  334. }
  335. }
  336. @Test
  337. public void testPackFormat_LargeObject() throws Exception {
  338. final int type = Constants.OBJ_BLOB;
  339. byte[] data = getRng().nextBytes(streamThreshold + 5);
  340. ObjectId id = getId(type, data);
  341. write(id, compressPackFormat(type, data));
  342. ObjectLoader ol;
  343. try (FileInputStream fs = new FileInputStream(path(id))) {
  344. ol = UnpackedObject.open(fs, path(id), id, wc);
  345. }
  346. assertNotNull("created loader", ol);
  347. assertEquals(type, ol.getType());
  348. assertEquals(data.length, ol.getSize());
  349. assertTrue("is large", ol.isLarge());
  350. try {
  351. ol.getCachedBytes();
  352. fail("Should have thrown LargeObjectException");
  353. } catch (LargeObjectException tooBig) {
  354. assertEquals(MessageFormat.format(
  355. JGitText.get().largeObjectException, id.name()), tooBig
  356. .getMessage());
  357. }
  358. try (ObjectStream in = ol.openStream()) {
  359. assertNotNull("have stream", in);
  360. assertEquals(type, in.getType());
  361. assertEquals(data.length, in.getSize());
  362. byte[] data2 = new byte[data.length];
  363. IO.readFully(in, data2, 0, data.length);
  364. assertTrue("same content", Arrays.equals(data2, data));
  365. assertEquals("stream at EOF", -1, in.read());
  366. }
  367. }
  368. @Test
  369. public void testPackFormat_DeltaNotAllowed() throws Exception {
  370. ObjectId id = ObjectId.zeroId();
  371. byte[] data = getRng().nextBytes(300);
  372. try {
  373. byte[] gz = compressPackFormat(Constants.OBJ_OFS_DELTA, data);
  374. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  375. fail("Did not throw CorruptObjectException");
  376. } catch (CorruptObjectException coe) {
  377. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  378. id.name(), JGitText.get().corruptObjectInvalidType), coe
  379. .getMessage());
  380. }
  381. try {
  382. byte[] gz = compressPackFormat(Constants.OBJ_REF_DELTA, data);
  383. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  384. fail("Did not throw CorruptObjectException");
  385. } catch (CorruptObjectException coe) {
  386. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  387. id.name(), JGitText.get().corruptObjectInvalidType), coe
  388. .getMessage());
  389. }
  390. try {
  391. byte[] gz = compressPackFormat(Constants.OBJ_TYPE_5, data);
  392. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  393. fail("Did not throw CorruptObjectException");
  394. } catch (CorruptObjectException coe) {
  395. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  396. id.name(), JGitText.get().corruptObjectInvalidType), coe
  397. .getMessage());
  398. }
  399. try {
  400. byte[] gz = compressPackFormat(Constants.OBJ_EXT, data);
  401. UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
  402. fail("Did not throw CorruptObjectException");
  403. } catch (CorruptObjectException coe) {
  404. assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
  405. id.name(), JGitText.get().corruptObjectInvalidType), coe
  406. .getMessage());
  407. }
  408. }
  409. private static byte[] compressStandardFormat(int type, byte[] data)
  410. throws IOException {
  411. String typeString = Constants.typeString(type);
  412. String length = String.valueOf(data.length);
  413. return compressStandardFormat(typeString, length, data);
  414. }
  415. private static byte[] compressStandardFormat(String type, String length,
  416. byte[] data) throws IOException {
  417. ByteArrayOutputStream out = new ByteArrayOutputStream();
  418. DeflaterOutputStream d = new DeflaterOutputStream(out);
  419. d.write(Constants.encodeASCII(type));
  420. d.write(' ');
  421. d.write(Constants.encodeASCII(length));
  422. d.write(0);
  423. d.write(data);
  424. d.finish();
  425. return out.toByteArray();
  426. }
  427. private static byte[] compressPackFormat(int type, byte[] data)
  428. throws IOException {
  429. byte[] hdr = new byte[64];
  430. int rawLength = data.length;
  431. int nextLength = rawLength >>> 4;
  432. hdr[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F));
  433. rawLength = nextLength;
  434. int n = 1;
  435. while (rawLength > 0) {
  436. nextLength >>>= 7;
  437. hdr[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
  438. rawLength = nextLength;
  439. }
  440. final ByteArrayOutputStream out = new ByteArrayOutputStream();
  441. out.write(hdr, 0, n);
  442. DeflaterOutputStream d = new DeflaterOutputStream(out);
  443. d.write(data);
  444. d.finish();
  445. return out.toByteArray();
  446. }
  447. private File path(ObjectId id) {
  448. return repo.getObjectDatabase().fileFor(id);
  449. }
  450. private void write(ObjectId id, byte[] data) throws IOException {
  451. File path = path(id);
  452. FileUtils.mkdirs(path.getParentFile());
  453. try (FileOutputStream out = new FileOutputStream(path)) {
  454. out.write(data);
  455. }
  456. }
  457. private ObjectId getId(int type, byte[] data) {
  458. try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
  459. return formatter.idFor(type, data);
  460. }
  461. }
  462. }