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.

ReceivePack.java 34KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_DELETE_REFS;
  45. import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_OFS_DELTA;
  46. import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_REPORT_STATUS;
  47. import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_SIDE_BAND_64K;
  48. import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
  49. import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
  50. import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
  51. import java.io.EOFException;
  52. import java.io.IOException;
  53. import java.io.InputStream;
  54. import java.io.OutputStream;
  55. import java.io.OutputStreamWriter;
  56. import java.io.Writer;
  57. import java.text.MessageFormat;
  58. import java.util.ArrayList;
  59. import java.util.Collections;
  60. import java.util.HashSet;
  61. import java.util.List;
  62. import java.util.Map;
  63. import java.util.Set;
  64. import org.eclipse.jgit.JGitText;
  65. import org.eclipse.jgit.errors.MissingObjectException;
  66. import org.eclipse.jgit.errors.PackProtocolException;
  67. import org.eclipse.jgit.errors.UnpackException;
  68. import org.eclipse.jgit.lib.Config;
  69. import org.eclipse.jgit.lib.Config.SectionParser;
  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.ObjectIdSubclassMap;
  74. import org.eclipse.jgit.lib.ObjectInserter;
  75. import org.eclipse.jgit.lib.PersonIdent;
  76. import org.eclipse.jgit.lib.Ref;
  77. import org.eclipse.jgit.lib.RefUpdate;
  78. import org.eclipse.jgit.lib.Repository;
  79. import org.eclipse.jgit.revwalk.ObjectWalk;
  80. import org.eclipse.jgit.revwalk.RevBlob;
  81. import org.eclipse.jgit.revwalk.RevCommit;
  82. import org.eclipse.jgit.revwalk.RevFlag;
  83. import org.eclipse.jgit.revwalk.RevObject;
  84. import org.eclipse.jgit.revwalk.RevSort;
  85. import org.eclipse.jgit.revwalk.RevTree;
  86. import org.eclipse.jgit.revwalk.RevWalk;
  87. import org.eclipse.jgit.storage.file.PackLock;
  88. import org.eclipse.jgit.transport.ReceiveCommand.Result;
  89. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  90. import org.eclipse.jgit.util.io.InterruptTimer;
  91. import org.eclipse.jgit.util.io.TimeoutInputStream;
  92. import org.eclipse.jgit.util.io.TimeoutOutputStream;
  93. /**
  94. * Implements the server side of a push connection, receiving objects.
  95. */
  96. public class ReceivePack {
  97. /** Database we write the stored objects into. */
  98. private final Repository db;
  99. /** Revision traversal support over {@link #db}. */
  100. private final RevWalk walk;
  101. /**
  102. * Is the client connection a bi-directional socket or pipe?
  103. * <p>
  104. * If true, this class assumes it can perform multiple read and write cycles
  105. * with the client over the input and output streams. This matches the
  106. * functionality available with a standard TCP/IP connection, or a local
  107. * operating system or in-memory pipe.
  108. * <p>
  109. * If false, this class runs in a read everything then output results mode,
  110. * making it suitable for single round-trip systems RPCs such as HTTP.
  111. */
  112. private boolean biDirectionalPipe = true;
  113. /** Should an incoming transfer validate objects? */
  114. private boolean checkReceivedObjects;
  115. /** Should an incoming transfer permit create requests? */
  116. private boolean allowCreates;
  117. /** Should an incoming transfer permit delete requests? */
  118. private boolean allowDeletes;
  119. /** Should an incoming transfer permit non-fast-forward requests? */
  120. private boolean allowNonFastForwards;
  121. private boolean allowOfsDelta;
  122. /** Identity to record action as within the reflog. */
  123. private PersonIdent refLogIdent;
  124. /** Filter used while advertising the refs to the client. */
  125. private RefFilter refFilter;
  126. /** Hook to validate the update commands before execution. */
  127. private PreReceiveHook preReceive;
  128. /** Hook to report on the commands after execution. */
  129. private PostReceiveHook postReceive;
  130. /** Timeout in seconds to wait for client interaction. */
  131. private int timeout;
  132. /** Timer to manage {@link #timeout}. */
  133. private InterruptTimer timer;
  134. private TimeoutInputStream timeoutIn;
  135. private InputStream rawIn;
  136. private OutputStream rawOut;
  137. private PacketLineIn pckIn;
  138. private PacketLineOut pckOut;
  139. private Writer msgs;
  140. private PackParser parser;
  141. /** The refs we advertised as existing at the start of the connection. */
  142. private Map<String, Ref> refs;
  143. /** Capabilities requested by the client. */
  144. private Set<String> enabledCapablities;
  145. /** Commands to execute, as received by the client. */
  146. private List<ReceiveCommand> commands;
  147. /** Error to display instead of advertising the references. */
  148. private StringBuilder advertiseError;
  149. /** An exception caught while unpacking and fsck'ing the objects. */
  150. private Throwable unpackError;
  151. /** If {@link BasePackPushConnection#CAPABILITY_REPORT_STATUS} is enabled. */
  152. private boolean reportStatus;
  153. /** If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled. */
  154. private boolean sideBand;
  155. /** Lock around the received pack file, while updating refs. */
  156. private PackLock packLock;
  157. private boolean checkReferencedIsReachable;
  158. /**
  159. * Create a new pack receive for an open repository.
  160. *
  161. * @param into
  162. * the destination repository.
  163. */
  164. public ReceivePack(final Repository into) {
  165. db = into;
  166. walk = new RevWalk(db);
  167. final ReceiveConfig cfg = db.getConfig().get(ReceiveConfig.KEY);
  168. checkReceivedObjects = cfg.checkReceivedObjects;
  169. allowCreates = cfg.allowCreates;
  170. allowDeletes = cfg.allowDeletes;
  171. allowNonFastForwards = cfg.allowNonFastForwards;
  172. allowOfsDelta = cfg.allowOfsDelta;
  173. refFilter = RefFilter.DEFAULT;
  174. preReceive = PreReceiveHook.NULL;
  175. postReceive = PostReceiveHook.NULL;
  176. }
  177. private static class ReceiveConfig {
  178. static final SectionParser<ReceiveConfig> KEY = new SectionParser<ReceiveConfig>() {
  179. public ReceiveConfig parse(final Config cfg) {
  180. return new ReceiveConfig(cfg);
  181. }
  182. };
  183. final boolean checkReceivedObjects;
  184. final boolean allowCreates;
  185. final boolean allowDeletes;
  186. final boolean allowNonFastForwards;
  187. final boolean allowOfsDelta;
  188. ReceiveConfig(final Config config) {
  189. checkReceivedObjects = config.getBoolean("receive", "fsckobjects",
  190. false);
  191. allowCreates = true;
  192. allowDeletes = !config.getBoolean("receive", "denydeletes", false);
  193. allowNonFastForwards = !config.getBoolean("receive",
  194. "denynonfastforwards", false);
  195. allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset",
  196. true);
  197. }
  198. }
  199. /** @return the repository this receive completes into. */
  200. public final Repository getRepository() {
  201. return db;
  202. }
  203. /** @return the RevWalk instance used by this connection. */
  204. public final RevWalk getRevWalk() {
  205. return walk;
  206. }
  207. /** @return all refs which were advertised to the client. */
  208. public final Map<String, Ref> getAdvertisedRefs() {
  209. return refs;
  210. }
  211. /**
  212. * @return true if this instance will validate all referenced, but not
  213. * supplied by the client, objects are reachable from another
  214. * reference.
  215. */
  216. public boolean isCheckReferencedObjectsAreReachable() {
  217. return checkReferencedIsReachable;
  218. }
  219. /**
  220. * Validate all referenced but not supplied objects are reachable.
  221. * <p>
  222. * If enabled, this instance will verify that references to objects not
  223. * contained within the received pack are already reachable through at least
  224. * one other reference selected by the {@link #getRefFilter()} and displayed
  225. * as part of {@link #getAdvertisedRefs()}.
  226. * <p>
  227. * This feature is useful when the application doesn't trust the client to
  228. * not provide a forged SHA-1 reference to an object, in an attempt to
  229. * access parts of the DAG that they aren't allowed to see and which have
  230. * been hidden from them via the configured {@link RefFilter}.
  231. * <p>
  232. * Enabling this feature may imply at least some, if not all, of the same
  233. * functionality performed by {@link #setCheckReceivedObjects(boolean)}.
  234. * Applications are encouraged to enable both features, if desired.
  235. *
  236. * @param b
  237. * {@code true} to enable the additional check.
  238. */
  239. public void setCheckReferencedObjectsAreReachable(boolean b) {
  240. this.checkReferencedIsReachable = b;
  241. }
  242. /**
  243. * @return true if this class expects a bi-directional pipe opened between
  244. * the client and itself. The default is true.
  245. */
  246. public boolean isBiDirectionalPipe() {
  247. return biDirectionalPipe;
  248. }
  249. /**
  250. * @param twoWay
  251. * if true, this class will assume the socket is a fully
  252. * bidirectional pipe between the two peers and takes advantage
  253. * of that by first transmitting the known refs, then waiting to
  254. * read commands. If false, this class assumes it must read the
  255. * commands before writing output and does not perform the
  256. * initial advertising.
  257. */
  258. public void setBiDirectionalPipe(final boolean twoWay) {
  259. biDirectionalPipe = twoWay;
  260. }
  261. /**
  262. * @return true if this instance will verify received objects are formatted
  263. * correctly. Validating objects requires more CPU time on this side
  264. * of the connection.
  265. */
  266. public boolean isCheckReceivedObjects() {
  267. return checkReceivedObjects;
  268. }
  269. /**
  270. * @param check
  271. * true to enable checking received objects; false to assume all
  272. * received objects are valid.
  273. */
  274. public void setCheckReceivedObjects(final boolean check) {
  275. checkReceivedObjects = check;
  276. }
  277. /** @return true if the client can request refs to be created. */
  278. public boolean isAllowCreates() {
  279. return allowCreates;
  280. }
  281. /**
  282. * @param canCreate
  283. * true to permit create ref commands to be processed.
  284. */
  285. public void setAllowCreates(final boolean canCreate) {
  286. allowCreates = canCreate;
  287. }
  288. /** @return true if the client can request refs to be deleted. */
  289. public boolean isAllowDeletes() {
  290. return allowDeletes;
  291. }
  292. /**
  293. * @param canDelete
  294. * true to permit delete ref commands to be processed.
  295. */
  296. public void setAllowDeletes(final boolean canDelete) {
  297. allowDeletes = canDelete;
  298. }
  299. /**
  300. * @return true if the client can request non-fast-forward updates of a ref,
  301. * possibly making objects unreachable.
  302. */
  303. public boolean isAllowNonFastForwards() {
  304. return allowNonFastForwards;
  305. }
  306. /**
  307. * @param canRewind
  308. * true to permit the client to ask for non-fast-forward updates
  309. * of an existing ref.
  310. */
  311. public void setAllowNonFastForwards(final boolean canRewind) {
  312. allowNonFastForwards = canRewind;
  313. }
  314. /** @return identity of the user making the changes in the reflog. */
  315. public PersonIdent getRefLogIdent() {
  316. return refLogIdent;
  317. }
  318. /**
  319. * Set the identity of the user appearing in the affected reflogs.
  320. * <p>
  321. * The timestamp portion of the identity is ignored. A new identity with the
  322. * current timestamp will be created automatically when the updates occur
  323. * and the log records are written.
  324. *
  325. * @param pi
  326. * identity of the user. If null the identity will be
  327. * automatically determined based on the repository
  328. * configuration.
  329. */
  330. public void setRefLogIdent(final PersonIdent pi) {
  331. refLogIdent = pi;
  332. }
  333. /** @return the filter used while advertising the refs to the client */
  334. public RefFilter getRefFilter() {
  335. return refFilter;
  336. }
  337. /**
  338. * Set the filter used while advertising the refs to the client.
  339. * <p>
  340. * Only refs allowed by this filter will be shown to the client.
  341. * Clients may still attempt to create or update a reference hidden
  342. * by the configured {@link RefFilter}. These attempts should be
  343. * rejected by a matching {@link PreReceiveHook}.
  344. *
  345. * @param refFilter
  346. * the filter; may be null to show all refs.
  347. */
  348. public void setRefFilter(final RefFilter refFilter) {
  349. this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  350. }
  351. /** @return get the hook invoked before updates occur. */
  352. public PreReceiveHook getPreReceiveHook() {
  353. return preReceive;
  354. }
  355. /**
  356. * Set the hook which is invoked prior to commands being executed.
  357. * <p>
  358. * Only valid commands (those which have no obvious errors according to the
  359. * received input and this instance's configuration) are passed into the
  360. * hook. The hook may mark a command with a result of any value other than
  361. * {@link Result#NOT_ATTEMPTED} to block its execution.
  362. * <p>
  363. * The hook may be called with an empty command collection if the current
  364. * set is completely invalid.
  365. *
  366. * @param h
  367. * the hook instance; may be null to disable the hook.
  368. */
  369. public void setPreReceiveHook(final PreReceiveHook h) {
  370. preReceive = h != null ? h : PreReceiveHook.NULL;
  371. }
  372. /** @return get the hook invoked after updates occur. */
  373. public PostReceiveHook getPostReceiveHook() {
  374. return postReceive;
  375. }
  376. /**
  377. * Set the hook which is invoked after commands are executed.
  378. * <p>
  379. * Only successful commands (type is {@link Result#OK}) are passed into the
  380. * hook. The hook may be called with an empty command collection if the
  381. * current set all resulted in an error.
  382. *
  383. * @param h
  384. * the hook instance; may be null to disable the hook.
  385. */
  386. public void setPostReceiveHook(final PostReceiveHook h) {
  387. postReceive = h != null ? h : PostReceiveHook.NULL;
  388. }
  389. /** @return timeout (in seconds) before aborting an IO operation. */
  390. public int getTimeout() {
  391. return timeout;
  392. }
  393. /**
  394. * Set the timeout before willing to abort an IO call.
  395. *
  396. * @param seconds
  397. * number of seconds to wait (with no data transfer occurring)
  398. * before aborting an IO read or write operation with the
  399. * connected client.
  400. */
  401. public void setTimeout(final int seconds) {
  402. timeout = seconds;
  403. }
  404. /** @return all of the command received by the current request. */
  405. public List<ReceiveCommand> getAllCommands() {
  406. return Collections.unmodifiableList(commands);
  407. }
  408. /**
  409. * Send an error message to the client.
  410. * <p>
  411. * If any error messages are sent before the references are advertised to
  412. * the client, the errors will be sent instead of the advertisement and the
  413. * receive operation will be aborted. All clients should receive and display
  414. * such early stage errors.
  415. * <p>
  416. * If the reference advertisements have already been sent, messages are sent
  417. * in a side channel. If the client doesn't support receiving messages, the
  418. * message will be discarded, with no other indication to the caller or to
  419. * the client.
  420. * <p>
  421. * {@link PreReceiveHook}s should always try to use
  422. * {@link ReceiveCommand#setResult(Result, String)} with a result status of
  423. * {@link Result#REJECTED_OTHER_REASON} to indicate any reasons for
  424. * rejecting an update. Messages attached to a command are much more likely
  425. * to be returned to the client.
  426. *
  427. * @param what
  428. * string describing the problem identified by the hook. The
  429. * string must not end with an LF, and must not contain an LF.
  430. */
  431. public void sendError(final String what) {
  432. if (refs == null) {
  433. if (advertiseError == null)
  434. advertiseError = new StringBuilder();
  435. advertiseError.append(what).append('\n');
  436. } else {
  437. try {
  438. if (msgs != null)
  439. msgs.write("error: " + what + "\n");
  440. } catch (IOException e) {
  441. // Ignore write failures.
  442. }
  443. }
  444. }
  445. /**
  446. * Send a message to the client, if it supports receiving them.
  447. * <p>
  448. * If the client doesn't support receiving messages, the message will be
  449. * discarded, with no other indication to the caller or to the client.
  450. *
  451. * @param what
  452. * string describing the problem identified by the hook. The
  453. * string must not end with an LF, and must not contain an LF.
  454. */
  455. public void sendMessage(final String what) {
  456. try {
  457. if (msgs != null)
  458. msgs.write(what + "\n");
  459. } catch (IOException e) {
  460. // Ignore write failures.
  461. }
  462. }
  463. /**
  464. * Execute the receive task on the socket.
  465. *
  466. * @param input
  467. * raw input to read client commands and pack data from. Caller
  468. * must ensure the input is buffered, otherwise read performance
  469. * may suffer.
  470. * @param output
  471. * response back to the Git network client. Caller must ensure
  472. * the output is buffered, otherwise write performance may
  473. * suffer.
  474. * @param messages
  475. * secondary "notice" channel to send additional messages out
  476. * through. When run over SSH this should be tied back to the
  477. * standard error channel of the command execution. For most
  478. * other network connections this should be null.
  479. * @throws IOException
  480. */
  481. public void receive(final InputStream input, final OutputStream output,
  482. final OutputStream messages) throws IOException {
  483. try {
  484. rawIn = input;
  485. rawOut = output;
  486. if (timeout > 0) {
  487. final Thread caller = Thread.currentThread();
  488. timer = new InterruptTimer(caller.getName() + "-Timer");
  489. timeoutIn = new TimeoutInputStream(rawIn, timer);
  490. TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
  491. timeoutIn.setTimeout(timeout * 1000);
  492. o.setTimeout(timeout * 1000);
  493. rawIn = timeoutIn;
  494. rawOut = o;
  495. }
  496. pckIn = new PacketLineIn(rawIn);
  497. pckOut = new PacketLineOut(rawOut);
  498. if (messages != null)
  499. msgs = new OutputStreamWriter(messages, Constants.CHARSET);
  500. enabledCapablities = new HashSet<String>();
  501. commands = new ArrayList<ReceiveCommand>();
  502. service();
  503. } finally {
  504. walk.release();
  505. try {
  506. if (pckOut != null)
  507. pckOut.flush();
  508. if (msgs != null)
  509. msgs.flush();
  510. if (sideBand) {
  511. // If we are using side band, we need to send a final
  512. // flush-pkt to tell the remote peer the side band is
  513. // complete and it should stop decoding. We need to
  514. // use the original output stream as rawOut is now the
  515. // side band data channel.
  516. //
  517. new PacketLineOut(output).end();
  518. }
  519. } finally {
  520. unlockPack();
  521. timeoutIn = null;
  522. rawIn = null;
  523. rawOut = null;
  524. pckIn = null;
  525. pckOut = null;
  526. msgs = null;
  527. refs = null;
  528. enabledCapablities = null;
  529. commands = null;
  530. if (timer != null) {
  531. try {
  532. timer.terminate();
  533. } finally {
  534. timer = null;
  535. }
  536. }
  537. }
  538. }
  539. }
  540. private void service() throws IOException {
  541. if (biDirectionalPipe)
  542. sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  543. else
  544. refs = refFilter.filter(db.getAllRefs());
  545. if (advertiseError != null)
  546. return;
  547. recvCommands();
  548. if (!commands.isEmpty()) {
  549. enableCapabilities();
  550. if (needPack()) {
  551. try {
  552. receivePack();
  553. if (needCheckConnectivity())
  554. checkConnectivity();
  555. parser = null;
  556. unpackError = null;
  557. } catch (IOException err) {
  558. unpackError = err;
  559. } catch (RuntimeException err) {
  560. unpackError = err;
  561. } catch (Error err) {
  562. unpackError = err;
  563. }
  564. }
  565. if (unpackError == null) {
  566. validateCommands();
  567. executeCommands();
  568. }
  569. unlockPack();
  570. if (reportStatus) {
  571. sendStatusReport(true, new Reporter() {
  572. void sendString(final String s) throws IOException {
  573. pckOut.writeString(s + "\n");
  574. }
  575. });
  576. pckOut.end();
  577. } else if (msgs != null) {
  578. sendStatusReport(false, new Reporter() {
  579. void sendString(final String s) throws IOException {
  580. msgs.write(s + "\n");
  581. }
  582. });
  583. }
  584. postReceive.onPostReceive(this, filterCommands(Result.OK));
  585. if (unpackError != null)
  586. throw new UnpackException(unpackError);
  587. }
  588. }
  589. private void unlockPack() throws IOException {
  590. if (packLock != null) {
  591. packLock.unlock();
  592. packLock = null;
  593. }
  594. }
  595. /**
  596. * Generate an advertisement of available refs and capabilities.
  597. *
  598. * @param adv
  599. * the advertisement formatter.
  600. * @throws IOException
  601. * the formatter failed to write an advertisement.
  602. */
  603. public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
  604. if (advertiseError != null) {
  605. adv.writeOne("ERR " + advertiseError);
  606. return;
  607. }
  608. final RevFlag advertised = walk.newFlag("ADVERTISED");
  609. adv.init(walk, advertised);
  610. adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
  611. adv.advertiseCapability(CAPABILITY_DELETE_REFS);
  612. adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
  613. if (allowOfsDelta)
  614. adv.advertiseCapability(CAPABILITY_OFS_DELTA);
  615. refs = refFilter.filter(db.getAllRefs());
  616. final Ref head = refs.remove(Constants.HEAD);
  617. adv.send(refs);
  618. if (head != null && !head.isSymbolic())
  619. adv.advertiseHave(head.getObjectId());
  620. adv.includeAdditionalHaves(db);
  621. if (adv.isEmpty())
  622. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
  623. adv.end();
  624. }
  625. private void recvCommands() throws IOException {
  626. for (;;) {
  627. String line;
  628. try {
  629. line = pckIn.readStringRaw();
  630. } catch (EOFException eof) {
  631. if (commands.isEmpty())
  632. return;
  633. throw eof;
  634. }
  635. if (line == PacketLineIn.END)
  636. break;
  637. if (commands.isEmpty()) {
  638. final int nul = line.indexOf('\0');
  639. if (nul >= 0) {
  640. for (String c : line.substring(nul + 1).split(" "))
  641. enabledCapablities.add(c);
  642. line = line.substring(0, nul);
  643. }
  644. }
  645. if (line.length() < 83) {
  646. final String m = JGitText.get().errorInvalidProtocolWantedOldNewRef;
  647. sendError(m);
  648. throw new PackProtocolException(m);
  649. }
  650. final ObjectId oldId = ObjectId.fromString(line.substring(0, 40));
  651. final ObjectId newId = ObjectId.fromString(line.substring(41, 81));
  652. final String name = line.substring(82);
  653. final ReceiveCommand cmd = new ReceiveCommand(oldId, newId, name);
  654. if (name.equals(Constants.HEAD)) {
  655. cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
  656. } else {
  657. cmd.setRef(refs.get(cmd.getRefName()));
  658. }
  659. commands.add(cmd);
  660. }
  661. }
  662. private void enableCapabilities() {
  663. reportStatus = enabledCapablities.contains(CAPABILITY_REPORT_STATUS);
  664. sideBand = enabledCapablities.contains(CAPABILITY_SIDE_BAND_64K);
  665. if (sideBand) {
  666. OutputStream out = rawOut;
  667. rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
  668. pckOut = new PacketLineOut(rawOut);
  669. msgs = new OutputStreamWriter(new SideBandOutputStream(CH_PROGRESS,
  670. MAX_BUF, out), Constants.CHARSET);
  671. }
  672. }
  673. private boolean needPack() {
  674. for (final ReceiveCommand cmd : commands) {
  675. if (cmd.getType() != ReceiveCommand.Type.DELETE)
  676. return true;
  677. }
  678. return false;
  679. }
  680. private void receivePack() throws IOException {
  681. // It might take the client a while to pack the objects it needs
  682. // to send to us. We should increase our timeout so we don't
  683. // abort while the client is computing.
  684. //
  685. if (timeoutIn != null)
  686. timeoutIn.setTimeout(10 * timeout * 1000);
  687. ObjectInserter ins = db.newObjectInserter();
  688. try {
  689. String lockMsg = "jgit receive-pack";
  690. if (getRefLogIdent() != null)
  691. lockMsg += " from " + getRefLogIdent().toExternalString();
  692. parser = ins.newPackParser(rawIn);
  693. parser.setAllowThin(true);
  694. parser.setNeedNewObjectIds(checkReferencedIsReachable);
  695. parser.setNeedBaseObjectIds(checkReferencedIsReachable);
  696. parser.setObjectChecking(isCheckReceivedObjects());
  697. parser.setLockMessage(lockMsg);
  698. packLock = parser.parse(NullProgressMonitor.INSTANCE);
  699. ins.flush();
  700. } finally {
  701. ins.release();
  702. }
  703. if (timeoutIn != null)
  704. timeoutIn.setTimeout(timeout * 1000);
  705. }
  706. private boolean needCheckConnectivity() {
  707. return isCheckReceivedObjects()
  708. || isCheckReferencedObjectsAreReachable();
  709. }
  710. private void checkConnectivity() throws IOException {
  711. ObjectIdSubclassMap<ObjectId> baseObjects = null;
  712. ObjectIdSubclassMap<ObjectId> providedObjects = null;
  713. if (checkReferencedIsReachable) {
  714. baseObjects = parser.getBaseObjectIds();
  715. providedObjects = parser.getNewObjectIds();
  716. }
  717. parser = null;
  718. final ObjectWalk ow = new ObjectWalk(db);
  719. ow.setRetainBody(false);
  720. if (checkReferencedIsReachable) {
  721. ow.sort(RevSort.TOPO);
  722. if (!baseObjects.isEmpty())
  723. ow.sort(RevSort.BOUNDARY, true);
  724. }
  725. for (final ReceiveCommand cmd : commands) {
  726. if (cmd.getResult() != Result.NOT_ATTEMPTED)
  727. continue;
  728. if (cmd.getType() == ReceiveCommand.Type.DELETE)
  729. continue;
  730. ow.markStart(ow.parseAny(cmd.getNewId()));
  731. }
  732. for (final Ref ref : refs.values()) {
  733. RevObject o = ow.parseAny(ref.getObjectId());
  734. ow.markUninteresting(o);
  735. if (checkReferencedIsReachable && !baseObjects.isEmpty()) {
  736. o = ow.peel(o);
  737. if (o instanceof RevCommit)
  738. o = ((RevCommit) o).getTree();
  739. if (o instanceof RevTree)
  740. ow.markUninteresting(o);
  741. }
  742. }
  743. RevCommit c;
  744. while ((c = ow.next()) != null) {
  745. if (checkReferencedIsReachable //
  746. && !c.has(RevFlag.UNINTERESTING) //
  747. && !providedObjects.contains(c))
  748. throw new MissingObjectException(c, Constants.TYPE_COMMIT);
  749. }
  750. RevObject o;
  751. while ((o = ow.nextObject()) != null) {
  752. if (o.has(RevFlag.UNINTERESTING))
  753. continue;
  754. if (checkReferencedIsReachable) {
  755. if (providedObjects.contains(o))
  756. continue;
  757. else
  758. throw new MissingObjectException(o, o.getType());
  759. }
  760. if (o instanceof RevBlob && !db.hasObject(o))
  761. throw new MissingObjectException(o, Constants.TYPE_BLOB);
  762. }
  763. if (checkReferencedIsReachable) {
  764. for (ObjectId id : baseObjects) {
  765. o = ow.parseAny(id);
  766. if (!o.has(RevFlag.UNINTERESTING))
  767. throw new MissingObjectException(o, o.getType());
  768. }
  769. }
  770. }
  771. private void validateCommands() {
  772. for (final ReceiveCommand cmd : commands) {
  773. final Ref ref = cmd.getRef();
  774. if (cmd.getResult() != Result.NOT_ATTEMPTED)
  775. continue;
  776. if (cmd.getType() == ReceiveCommand.Type.DELETE
  777. && !isAllowDeletes()) {
  778. // Deletes are not supported on this repository.
  779. //
  780. cmd.setResult(Result.REJECTED_NODELETE);
  781. continue;
  782. }
  783. if (cmd.getType() == ReceiveCommand.Type.CREATE) {
  784. if (!isAllowCreates()) {
  785. cmd.setResult(Result.REJECTED_NOCREATE);
  786. continue;
  787. }
  788. if (ref != null && !isAllowNonFastForwards()) {
  789. // Creation over an existing ref is certainly not going
  790. // to be a fast-forward update. We can reject it early.
  791. //
  792. cmd.setResult(Result.REJECTED_NONFASTFORWARD);
  793. continue;
  794. }
  795. if (ref != null) {
  796. // A well behaved client shouldn't have sent us a
  797. // create command for a ref we advertised to it.
  798. //
  799. cmd.setResult(Result.REJECTED_OTHER_REASON, "ref exists");
  800. continue;
  801. }
  802. }
  803. if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null
  804. && !ObjectId.zeroId().equals(cmd.getOldId())
  805. && !ref.getObjectId().equals(cmd.getOldId())) {
  806. // Delete commands can be sent with the old id matching our
  807. // advertised value, *OR* with the old id being 0{40}. Any
  808. // other requested old id is invalid.
  809. //
  810. cmd.setResult(Result.REJECTED_OTHER_REASON,
  811. JGitText.get().invalidOldIdSent);
  812. continue;
  813. }
  814. if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
  815. if (ref == null) {
  816. // The ref must have been advertised in order to be updated.
  817. //
  818. cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef);
  819. continue;
  820. }
  821. if (!ref.getObjectId().equals(cmd.getOldId())) {
  822. // A properly functioning client will send the same
  823. // object id we advertised.
  824. //
  825. cmd.setResult(Result.REJECTED_OTHER_REASON,
  826. JGitText.get().invalidOldIdSent);
  827. continue;
  828. }
  829. // Is this possibly a non-fast-forward style update?
  830. //
  831. RevObject oldObj, newObj;
  832. try {
  833. oldObj = walk.parseAny(cmd.getOldId());
  834. } catch (IOException e) {
  835. cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
  836. .getOldId().name());
  837. continue;
  838. }
  839. try {
  840. newObj = walk.parseAny(cmd.getNewId());
  841. } catch (IOException e) {
  842. cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
  843. .getNewId().name());
  844. continue;
  845. }
  846. if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
  847. try {
  848. if (!walk.isMergedInto((RevCommit) oldObj,
  849. (RevCommit) newObj)) {
  850. cmd
  851. .setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
  852. }
  853. } catch (MissingObjectException e) {
  854. cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
  855. .getMessage());
  856. } catch (IOException e) {
  857. cmd.setResult(Result.REJECTED_OTHER_REASON);
  858. }
  859. } else {
  860. cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
  861. }
  862. }
  863. if (!cmd.getRefName().startsWith(Constants.R_REFS)
  864. || !Repository.isValidRefName(cmd.getRefName())) {
  865. cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().funnyRefname);
  866. }
  867. }
  868. }
  869. private void executeCommands() {
  870. preReceive.onPreReceive(this, filterCommands(Result.NOT_ATTEMPTED));
  871. for (final ReceiveCommand cmd : filterCommands(Result.NOT_ATTEMPTED))
  872. execute(cmd);
  873. }
  874. private void execute(final ReceiveCommand cmd) {
  875. try {
  876. final RefUpdate ru = db.updateRef(cmd.getRefName());
  877. ru.setRefLogIdent(getRefLogIdent());
  878. switch (cmd.getType()) {
  879. case DELETE:
  880. if (!ObjectId.zeroId().equals(cmd.getOldId())) {
  881. // We can only do a CAS style delete if the client
  882. // didn't bork its delete request by sending the
  883. // wrong zero id rather than the advertised one.
  884. //
  885. ru.setExpectedOldObjectId(cmd.getOldId());
  886. }
  887. ru.setForceUpdate(true);
  888. status(cmd, ru.delete(walk));
  889. break;
  890. case CREATE:
  891. case UPDATE:
  892. case UPDATE_NONFASTFORWARD:
  893. ru.setForceUpdate(isAllowNonFastForwards());
  894. ru.setExpectedOldObjectId(cmd.getOldId());
  895. ru.setNewObjectId(cmd.getNewId());
  896. ru.setRefLogMessage("push", true);
  897. status(cmd, ru.update(walk));
  898. break;
  899. }
  900. } catch (IOException err) {
  901. cmd.setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
  902. JGitText.get().lockError, err.getMessage()));
  903. }
  904. }
  905. private void status(final ReceiveCommand cmd, final RefUpdate.Result result) {
  906. switch (result) {
  907. case NOT_ATTEMPTED:
  908. cmd.setResult(Result.NOT_ATTEMPTED);
  909. break;
  910. case LOCK_FAILURE:
  911. case IO_FAILURE:
  912. cmd.setResult(Result.LOCK_FAILURE);
  913. break;
  914. case NO_CHANGE:
  915. case NEW:
  916. case FORCED:
  917. case FAST_FORWARD:
  918. cmd.setResult(Result.OK);
  919. break;
  920. case REJECTED:
  921. cmd.setResult(Result.REJECTED_NONFASTFORWARD);
  922. break;
  923. case REJECTED_CURRENT_BRANCH:
  924. cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
  925. break;
  926. default:
  927. cmd.setResult(Result.REJECTED_OTHER_REASON, result.name());
  928. break;
  929. }
  930. }
  931. private List<ReceiveCommand> filterCommands(final Result want) {
  932. final List<ReceiveCommand> r = new ArrayList<ReceiveCommand>(commands
  933. .size());
  934. for (final ReceiveCommand cmd : commands) {
  935. if (cmd.getResult() == want)
  936. r.add(cmd);
  937. }
  938. return r;
  939. }
  940. private void sendStatusReport(final boolean forClient, final Reporter out)
  941. throws IOException {
  942. if (unpackError != null) {
  943. out.sendString("unpack error " + unpackError.getMessage());
  944. if (forClient) {
  945. for (final ReceiveCommand cmd : commands) {
  946. out.sendString("ng " + cmd.getRefName()
  947. + " n/a (unpacker error)");
  948. }
  949. }
  950. return;
  951. }
  952. if (forClient)
  953. out.sendString("unpack ok");
  954. for (final ReceiveCommand cmd : commands) {
  955. if (cmd.getResult() == Result.OK) {
  956. if (forClient)
  957. out.sendString("ok " + cmd.getRefName());
  958. continue;
  959. }
  960. final StringBuilder r = new StringBuilder();
  961. r.append("ng ");
  962. r.append(cmd.getRefName());
  963. r.append(" ");
  964. switch (cmd.getResult()) {
  965. case NOT_ATTEMPTED:
  966. r.append("server bug; ref not processed");
  967. break;
  968. case REJECTED_NOCREATE:
  969. r.append("creation prohibited");
  970. break;
  971. case REJECTED_NODELETE:
  972. r.append("deletion prohibited");
  973. break;
  974. case REJECTED_NONFASTFORWARD:
  975. r.append("non-fast forward");
  976. break;
  977. case REJECTED_CURRENT_BRANCH:
  978. r.append("branch is currently checked out");
  979. break;
  980. case REJECTED_MISSING_OBJECT:
  981. if (cmd.getMessage() == null)
  982. r.append("missing object(s)");
  983. else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH)
  984. r.append("object " + cmd.getMessage() + " missing");
  985. else
  986. r.append(cmd.getMessage());
  987. break;
  988. case REJECTED_OTHER_REASON:
  989. if (cmd.getMessage() == null)
  990. r.append("unspecified reason");
  991. else
  992. r.append(cmd.getMessage());
  993. break;
  994. case LOCK_FAILURE:
  995. r.append("failed to lock");
  996. break;
  997. case OK:
  998. // We shouldn't have reached this case (see 'ok' case above).
  999. continue;
  1000. }
  1001. out.sendString(r.toString());
  1002. }
  1003. }
  1004. static abstract class Reporter {
  1005. abstract void sendString(String s) throws IOException;
  1006. }
  1007. }