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.

ReceivePackAdvertiseRefsHookTest.java 21KB

Fix checkReferencedIsReachable to use correct base list When checkReferencedIsReachable is set in ReceivePack we are trying to prove that the push client is permitted to access an object that it did not send to us, but that the received objects link to either via a link inside of an object (e.g. commit parent pointer or tree member) or by a delta base reference. To do this check we are making a list of every potential delta base, and then ensuring that every delta base used appears on this list. If a delta base does not appear on this list, we abort with an error, letting the client know we are missing a particular object. Preventing spurious errors about missing delta base objects requires us to use the exact same list of potential delta bases as the remote push client used. This means we must use TOPO ordering, and we need to enable BOUNDARY sorting so that ObjectWalk will correctly include any trees found during the enumeration back to the common merge base between the interesting and uninteresting heads. To ensure JGit's own push client matches this same potential delta base list, we need to undo 60aae90d4d15 ("Disable topological sorting in PackWriter") and switch back to using the conventional TOPO ordering for commits in a pack file. This ensures that our own push client will use the same potential base object list as checkReferencedIsReachable uses on the receiving side. Change-Id: I14d0a326deb62a43f987b375cfe519711031e172 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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.transport;
  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.assertNull;
  15. import static org.junit.Assert.assertSame;
  16. import static org.junit.Assert.assertTrue;
  17. import static org.junit.Assert.fail;
  18. import java.io.ByteArrayInputStream;
  19. import java.io.IOException;
  20. import java.net.URISyntaxException;
  21. import java.security.MessageDigest;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.concurrent.atomic.AtomicReference;
  27. import java.util.zip.Deflater;
  28. import org.eclipse.jgit.errors.MissingObjectException;
  29. import org.eclipse.jgit.errors.UnpackException;
  30. import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
  31. import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
  32. import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
  33. import org.eclipse.jgit.junit.TestRepository;
  34. import org.eclipse.jgit.lib.Constants;
  35. import org.eclipse.jgit.lib.NullProgressMonitor;
  36. import org.eclipse.jgit.lib.ObjectId;
  37. import org.eclipse.jgit.lib.ObjectInserter;
  38. import org.eclipse.jgit.lib.ObjectLoader;
  39. import org.eclipse.jgit.lib.Ref;
  40. import org.eclipse.jgit.lib.Repository;
  41. import org.eclipse.jgit.revwalk.RevBlob;
  42. import org.eclipse.jgit.revwalk.RevCommit;
  43. import org.eclipse.jgit.revwalk.RevTree;
  44. import org.eclipse.jgit.revwalk.RevWalk;
  45. import org.eclipse.jgit.util.NB;
  46. import org.eclipse.jgit.util.TemporaryBuffer;
  47. import org.junit.After;
  48. import org.junit.Before;
  49. import org.junit.Test;
  50. public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCase {
  51. private static final NullProgressMonitor PM = NullProgressMonitor.INSTANCE;
  52. private static final String R_MASTER = Constants.R_HEADS + Constants.MASTER;
  53. private static final String R_PRIVATE = Constants.R_HEADS + "private";
  54. private Repository src;
  55. private Repository dst;
  56. private RevCommit A, B, P;
  57. private RevBlob a, b;
  58. @Override
  59. @Before
  60. public void setUp() throws Exception {
  61. super.setUp();
  62. src = createBareRepository();
  63. dst = createBareRepository();
  64. // Fill dst with a some common history.
  65. //
  66. try (TestRepository<Repository> d = new TestRepository<>(dst)) {
  67. a = d.blob("a");
  68. A = d.commit(d.tree(d.file("a", a)));
  69. B = d.commit().parent(A).create();
  70. d.update(R_MASTER, B);
  71. // Clone from dst into src
  72. //
  73. try (Transport t = Transport.open(src, uriOf(dst))) {
  74. t.fetch(PM,
  75. Collections.singleton(new RefSpec("+refs/*:refs/*")));
  76. assertEquals(B, src.resolve(R_MASTER));
  77. }
  78. // Now put private stuff into dst.
  79. //
  80. b = d.blob("b");
  81. P = d.commit(d.tree(d.file("b", b)), A);
  82. d.update(R_PRIVATE, P);
  83. }
  84. }
  85. @Test
  86. public void testFilterHidesPrivate() throws Exception {
  87. Map<String, Ref> refs;
  88. try (TransportLocal t = new TransportLocal(src, uriOf(dst),
  89. dst.getDirectory()) {
  90. @Override
  91. ReceivePack createReceivePack(Repository db) {
  92. db.close();
  93. dst.incrementOpen();
  94. final ReceivePack rp = super.createReceivePack(dst);
  95. rp.setAdvertiseRefsHook(new HidePrivateHook());
  96. return rp;
  97. }
  98. }) {
  99. try (PushConnection c = t.openPush()) {
  100. refs = c.getRefsMap();
  101. }
  102. }
  103. assertNotNull(refs);
  104. assertNull("no private", refs.get(R_PRIVATE));
  105. assertNull("no HEAD", refs.get(Constants.HEAD));
  106. assertEquals(1, refs.size());
  107. Ref master = refs.get(R_MASTER);
  108. assertNotNull("has master", master);
  109. assertEquals(B, master.getObjectId());
  110. }
  111. @Test
  112. public void resetsHaves() throws Exception {
  113. AtomicReference<Set<ObjectId>> haves = new AtomicReference<>();
  114. try (TransportLocal t = new TransportLocal(src, uriOf(dst),
  115. dst.getDirectory()) {
  116. @Override
  117. ReceivePack createReceivePack(Repository db) {
  118. dst.incrementOpen();
  119. ReceivePack rp = super.createReceivePack(dst);
  120. rp.setAdvertiseRefsHook(new AdvertiseRefsHook() {
  121. @Override
  122. public void advertiseRefs(ReceivePack rp2)
  123. throws ServiceMayNotContinueException {
  124. rp.setAdvertisedRefs(rp.getRepository().getAllRefs(),
  125. null);
  126. new HidePrivateHook().advertiseRefs(rp);
  127. haves.set(rp.getAdvertisedObjects());
  128. }
  129. @Override
  130. public void advertiseRefs(UploadPack uploadPack)
  131. throws ServiceMayNotContinueException {
  132. throw new UnsupportedOperationException();
  133. }
  134. });
  135. return rp;
  136. }
  137. }) {
  138. try (PushConnection c = t.openPush()) {
  139. // Just has to open/close for advertisement.
  140. }
  141. }
  142. assertEquals(1, haves.get().size());
  143. assertTrue(haves.get().contains(B));
  144. assertFalse(haves.get().contains(P));
  145. }
  146. private TransportLocal newTransportLocalWithStrictValidation()
  147. throws Exception {
  148. return new TransportLocal(src, uriOf(dst), dst.getDirectory()) {
  149. @Override
  150. ReceivePack createReceivePack(Repository db) {
  151. db.close();
  152. dst.incrementOpen();
  153. final ReceivePack rp = super.createReceivePack(dst);
  154. rp.setCheckReceivedObjects(true);
  155. rp.setCheckReferencedObjectsAreReachable(true);
  156. rp.setAdvertiseRefsHook(new HidePrivateHook());
  157. return rp;
  158. }
  159. };
  160. }
  161. @Test
  162. public void testSuccess() throws Exception {
  163. // Manually force a delta of an object so we reuse it later.
  164. //
  165. TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  166. packHeader(pack, 2);
  167. pack.write((Constants.OBJ_BLOB) << 4 | 1);
  168. deflate(pack, new byte[] { 'a' });
  169. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  170. a.copyRawTo(pack);
  171. deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
  172. digest(pack);
  173. openPack(pack);
  174. // Verify the only storage of b is our packed delta above.
  175. //
  176. ObjectDirectory od = (ObjectDirectory) src.getObjectDatabase();
  177. assertTrue("has b", od.has(b));
  178. assertFalse("b not loose", od.fileFor(b).exists());
  179. // Now use b but in a different commit than what is hidden.
  180. //
  181. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  182. RevCommit N = s.commit().parent(B).add("q", b).create();
  183. s.update(R_MASTER, N);
  184. // Push this new content to the remote, doing strict validation.
  185. //
  186. PushResult r;
  187. RemoteRefUpdate u = new RemoteRefUpdate( //
  188. src, //
  189. R_MASTER, // src name
  190. R_MASTER, // dst name
  191. false, // do not force update
  192. null, // local tracking branch
  193. null // expected id
  194. );
  195. try (TransportLocal t = newTransportLocalWithStrictValidation()) {
  196. t.setPushThin(true);
  197. r = t.push(PM, Collections.singleton(u));
  198. dst.close();
  199. }
  200. assertNotNull("have result", r);
  201. assertNull("private not advertised", r.getAdvertisedRef(R_PRIVATE));
  202. assertSame("master updated", RemoteRefUpdate.Status.OK,
  203. u.getStatus());
  204. assertEquals(N, dst.resolve(R_MASTER));
  205. }
  206. }
  207. @Test
  208. public void testCreateBranchAtHiddenCommitFails() throws Exception {
  209. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
  210. packHeader(pack, 0);
  211. digest(pack);
  212. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
  213. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  214. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + P.name() + ' '
  215. + "refs/heads/s" + '\0'
  216. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  217. inPckLine.end();
  218. pack.writeTo(inBuf, PM);
  219. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  220. final ReceivePack rp = new ReceivePack(dst);
  221. rp.setCheckReceivedObjects(true);
  222. rp.setCheckReferencedObjectsAreReachable(true);
  223. rp.setAdvertiseRefsHook(new HidePrivateHook());
  224. try {
  225. receive(rp, inBuf, outBuf);
  226. fail("Expected UnpackException");
  227. } catch (UnpackException failed) {
  228. Throwable err = failed.getCause();
  229. assertTrue(err instanceof MissingObjectException);
  230. MissingObjectException moe = (MissingObjectException) err;
  231. assertEquals(P, moe.getObjectId());
  232. }
  233. final PacketLineIn r = asPacketLineIn(outBuf);
  234. String master = r.readString();
  235. int nul = master.indexOf('\0');
  236. assertTrue("has capability list", nul > 0);
  237. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  238. assertTrue(PacketLineIn.isEnd(r.readString()));
  239. assertEquals("unpack error Missing commit " + P.name(), r.readString());
  240. assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
  241. assertTrue(PacketLineIn.isEnd(r.readString()));
  242. }
  243. private static void receive(final ReceivePack rp,
  244. final TemporaryBuffer.Heap inBuf, final TemporaryBuffer.Heap outBuf)
  245. throws IOException {
  246. rp.receive(new ByteArrayInputStream(inBuf.toByteArray()), outBuf, null);
  247. }
  248. @Test
  249. public void testUsingHiddenDeltaBaseFails() throws Exception {
  250. byte[] delta = { 0x1, 0x1, 0x1, 'c' };
  251. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  252. RevCommit N = s.commit().parent(B)
  253. .add("q",
  254. s.blob(BinaryDelta.apply(
  255. dst.open(b).getCachedBytes(), delta)))
  256. .create();
  257. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  258. packHeader(pack, 3);
  259. copy(pack, src.open(N));
  260. copy(pack, src.open(s.parseBody(N).getTree()));
  261. pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
  262. b.copyRawTo(pack);
  263. deflate(pack, delta);
  264. digest(pack);
  265. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
  266. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  267. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name()
  268. + ' ' + "refs/heads/s" + '\0'
  269. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  270. inPckLine.end();
  271. pack.writeTo(inBuf, PM);
  272. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  273. final ReceivePack rp = new ReceivePack(dst);
  274. rp.setCheckReceivedObjects(true);
  275. rp.setCheckReferencedObjectsAreReachable(true);
  276. rp.setAdvertiseRefsHook(new HidePrivateHook());
  277. try {
  278. receive(rp, inBuf, outBuf);
  279. fail("Expected UnpackException");
  280. } catch (UnpackException failed) {
  281. Throwable err = failed.getCause();
  282. assertTrue(err instanceof MissingObjectException);
  283. MissingObjectException moe = (MissingObjectException) err;
  284. assertEquals(b, moe.getObjectId());
  285. }
  286. final PacketLineIn r = asPacketLineIn(outBuf);
  287. String master = r.readString();
  288. int nul = master.indexOf('\0');
  289. assertTrue("has capability list", nul > 0);
  290. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  291. assertTrue(PacketLineIn.isEnd(r.readString()));
  292. assertEquals("unpack error Missing blob " + b.name(),
  293. r.readString());
  294. assertEquals("ng refs/heads/s n/a (unpacker error)",
  295. r.readString());
  296. assertTrue(PacketLineIn.isEnd(r.readString()));
  297. }
  298. }
  299. @Test
  300. public void testUsingHiddenCommonBlobFails() throws Exception {
  301. // Try to use the 'b' blob that is hidden.
  302. //
  303. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  304. RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create();
  305. // But don't include it in the pack.
  306. //
  307. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  308. packHeader(pack, 2);
  309. copy(pack, src.open(N));
  310. copy(pack, src.open(s.parseBody(N).getTree()));
  311. digest(pack);
  312. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
  313. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  314. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name()
  315. + ' ' + "refs/heads/s" + '\0'
  316. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  317. inPckLine.end();
  318. pack.writeTo(inBuf, PM);
  319. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  320. final ReceivePack rp = new ReceivePack(dst);
  321. rp.setCheckReceivedObjects(true);
  322. rp.setCheckReferencedObjectsAreReachable(true);
  323. rp.setAdvertiseRefsHook(new HidePrivateHook());
  324. try {
  325. receive(rp, inBuf, outBuf);
  326. fail("Expected UnpackException");
  327. } catch (UnpackException failed) {
  328. Throwable err = failed.getCause();
  329. assertTrue(err instanceof MissingObjectException);
  330. MissingObjectException moe = (MissingObjectException) err;
  331. assertEquals(b, moe.getObjectId());
  332. }
  333. final PacketLineIn r = asPacketLineIn(outBuf);
  334. String master = r.readString();
  335. int nul = master.indexOf('\0');
  336. assertTrue("has capability list", nul > 0);
  337. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  338. assertTrue(PacketLineIn.isEnd(r.readString()));
  339. assertEquals("unpack error Missing blob " + b.name(),
  340. r.readString());
  341. assertEquals("ng refs/heads/s n/a (unpacker error)",
  342. r.readString());
  343. assertTrue(PacketLineIn.isEnd(r.readString()));
  344. }
  345. }
  346. @Test
  347. public void testUsingUnknownBlobFails() throws Exception {
  348. // Try to use the 'n' blob that is not on the server.
  349. //
  350. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  351. RevBlob n = s.blob("n");
  352. RevCommit N = s.commit().parent(B).add("q", n).create();
  353. // But don't include it in the pack.
  354. //
  355. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  356. packHeader(pack, 2);
  357. copy(pack, src.open(N));
  358. copy(pack, src.open(s.parseBody(N).getTree()));
  359. digest(pack);
  360. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
  361. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  362. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name()
  363. + ' ' + "refs/heads/s" + '\0'
  364. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  365. inPckLine.end();
  366. pack.writeTo(inBuf, PM);
  367. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  368. final ReceivePack rp = new ReceivePack(dst);
  369. rp.setCheckReceivedObjects(true);
  370. rp.setCheckReferencedObjectsAreReachable(true);
  371. rp.setAdvertiseRefsHook(new HidePrivateHook());
  372. try {
  373. receive(rp, inBuf, outBuf);
  374. fail("Expected UnpackException");
  375. } catch (UnpackException failed) {
  376. Throwable err = failed.getCause();
  377. assertTrue(err instanceof MissingObjectException);
  378. MissingObjectException moe = (MissingObjectException) err;
  379. assertEquals(n, moe.getObjectId());
  380. }
  381. final PacketLineIn r = asPacketLineIn(outBuf);
  382. String master = r.readString();
  383. int nul = master.indexOf('\0');
  384. assertTrue("has capability list", nul > 0);
  385. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  386. assertTrue(PacketLineIn.isEnd(r.readString()));
  387. assertEquals("unpack error Missing blob " + n.name(),
  388. r.readString());
  389. assertEquals("ng refs/heads/s n/a (unpacker error)",
  390. r.readString());
  391. assertTrue(PacketLineIn.isEnd(r.readString()));
  392. }
  393. }
  394. @Test
  395. public void testIncludesInvalidGitmodules() throws Exception {
  396. final TemporaryBuffer.Heap inBuf = setupSourceRepoInvalidGitmodules();
  397. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  398. final ReceivePack rp = new ReceivePack(dst);
  399. rp.setCheckReceivedObjects(true);
  400. rp.setCheckReferencedObjectsAreReachable(true);
  401. rp.setAdvertiseRefsHook(new HidePrivateHook());
  402. try {
  403. receive(rp, inBuf, outBuf);
  404. fail("Expected UnpackException");
  405. } catch (UnpackException failed) {
  406. // Expected
  407. }
  408. final PacketLineIn r = asPacketLineIn(outBuf);
  409. String master = r.readString();
  410. int nul = master.indexOf('\0');
  411. assertTrue("has capability list", nul > 0);
  412. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  413. assertTrue(PacketLineIn.isEnd(r.readString()));
  414. String errorLine = r.readString();
  415. assertTrue(errorLine.startsWith("unpack error"));
  416. assertTrue(errorLine.contains("Invalid submodule URL '-"));
  417. assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
  418. assertTrue(PacketLineIn.isEnd(r.readString()));
  419. }
  420. private TemporaryBuffer.Heap setupSourceRepoInvalidGitmodules()
  421. throws IOException, Exception, MissingObjectException {
  422. String fakeGitmodules = new StringBuilder()
  423. .append("[submodule \"test\"]\n")
  424. .append(" path = xlib\n")
  425. .append(" url = https://example.com/repo/xlib.git\n\n")
  426. .append("[submodule \"test2\"]\n")
  427. .append(" path = zlib\n")
  428. .append(" url = -upayload.sh\n")
  429. .toString();
  430. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  431. RevBlob blob = s.blob(fakeGitmodules);
  432. RevCommit N = s.commit().parent(B).add(".gitmodules", blob)
  433. .create();
  434. RevTree t = s.parseBody(N).getTree();
  435. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  436. packHeader(pack, 3);
  437. copy(pack, src.open(N));
  438. copy(pack, src.open(t));
  439. copy(pack, src.open(blob));
  440. digest(pack);
  441. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
  442. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  443. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name()
  444. + ' ' + "refs/heads/s" + '\0'
  445. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  446. inPckLine.end();
  447. pack.writeTo(inBuf, PM);
  448. return inBuf;
  449. }
  450. }
  451. @Test
  452. public void testUsingUnknownTreeFails() throws Exception {
  453. try (TestRepository<Repository> s = new TestRepository<>(src)) {
  454. RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create();
  455. RevTree t = s.parseBody(N).getTree();
  456. // Don't include the tree in the pack.
  457. //
  458. final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
  459. packHeader(pack, 1);
  460. copy(pack, src.open(N));
  461. digest(pack);
  462. final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
  463. final PacketLineOut inPckLine = new PacketLineOut(inBuf);
  464. inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name()
  465. + ' ' + "refs/heads/s" + '\0'
  466. + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
  467. inPckLine.end();
  468. pack.writeTo(inBuf, PM);
  469. final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
  470. final ReceivePack rp = new ReceivePack(dst);
  471. rp.setCheckReceivedObjects(true);
  472. rp.setCheckReferencedObjectsAreReachable(true);
  473. rp.setAdvertiseRefsHook(new HidePrivateHook());
  474. try {
  475. receive(rp, inBuf, outBuf);
  476. fail("Expected UnpackException");
  477. } catch (UnpackException failed) {
  478. Throwable err = failed.getCause();
  479. assertTrue(err instanceof MissingObjectException);
  480. MissingObjectException moe = (MissingObjectException) err;
  481. assertEquals(t, moe.getObjectId());
  482. }
  483. final PacketLineIn r = asPacketLineIn(outBuf);
  484. String master = r.readString();
  485. int nul = master.indexOf('\0');
  486. assertTrue("has capability list", nul > 0);
  487. assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
  488. assertTrue(PacketLineIn.isEnd(r.readString()));
  489. assertEquals("unpack error Missing tree " + t.name(),
  490. r.readString());
  491. assertEquals("ng refs/heads/s n/a (unpacker error)",
  492. r.readString());
  493. assertTrue(PacketLineIn.isEnd(r.readString()));
  494. }
  495. }
  496. private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt)
  497. throws IOException {
  498. final byte[] hdr = new byte[8];
  499. NB.encodeInt32(hdr, 0, 2);
  500. NB.encodeInt32(hdr, 4, cnt);
  501. tinyPack.write(Constants.PACK_SIGNATURE);
  502. tinyPack.write(hdr, 0, 8);
  503. }
  504. private static void copy(TemporaryBuffer.Heap tinyPack, ObjectLoader ldr)
  505. throws IOException {
  506. final byte[] buf = new byte[64];
  507. final byte[] content = ldr.getCachedBytes();
  508. int dataLength = content.length;
  509. int nextLength = dataLength >>> 4;
  510. int size = 0;
  511. buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00)
  512. | (ldr.getType() << 4) | (dataLength & 0x0F));
  513. dataLength = nextLength;
  514. while (dataLength > 0) {
  515. nextLength >>>= 7;
  516. buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (dataLength & 0x7F));
  517. dataLength = nextLength;
  518. }
  519. tinyPack.write(buf, 0, size);
  520. deflate(tinyPack, content);
  521. }
  522. private static void deflate(TemporaryBuffer.Heap tinyPack,
  523. final byte[] content)
  524. throws IOException {
  525. final Deflater deflater = new Deflater();
  526. final byte[] buf = new byte[128];
  527. deflater.setInput(content, 0, content.length);
  528. deflater.finish();
  529. do {
  530. final int n = deflater.deflate(buf, 0, buf.length);
  531. if (n > 0)
  532. tinyPack.write(buf, 0, n);
  533. } while (!deflater.finished());
  534. }
  535. private static void digest(TemporaryBuffer.Heap buf) throws IOException {
  536. MessageDigest md = Constants.newMessageDigest();
  537. md.update(buf.toByteArray());
  538. buf.write(md.digest());
  539. }
  540. private ObjectInserter inserter;
  541. @After
  542. public void release() {
  543. if (inserter != null) {
  544. inserter.close();
  545. }
  546. }
  547. private void openPack(TemporaryBuffer.Heap buf) throws IOException {
  548. if (inserter == null)
  549. inserter = src.newObjectInserter();
  550. final byte[] raw = buf.toByteArray();
  551. PackParser p = inserter.newPackParser(new ByteArrayInputStream(raw));
  552. p.setAllowThin(true);
  553. p.parse(PM);
  554. }
  555. private static PacketLineIn asPacketLineIn(TemporaryBuffer.Heap buf)
  556. throws IOException {
  557. return new PacketLineIn(new ByteArrayInputStream(buf.toByteArray()));
  558. }
  559. private static final class HidePrivateHook extends AbstractAdvertiseRefsHook {
  560. @Override
  561. public Map<String, Ref> getAdvertisedRefs(Repository r, RevWalk revWalk) {
  562. Map<String, Ref> refs = new HashMap<>(r.getAllRefs());
  563. assertNotNull(refs.remove(R_PRIVATE));
  564. return refs;
  565. }
  566. }
  567. private static URIish uriOf(Repository r) throws URISyntaxException {
  568. return new URIish(r.getDirectory().getAbsolutePath());
  569. }
  570. }