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.

IndexPack.java 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5. * and other copyright owners as documented in the project's IP log.
  6. *
  7. * This program and the accompanying materials are made available
  8. * under the terms of the Eclipse Distribution License v1.0 which
  9. * accompanies this distribution, is reproduced below, and is
  10. * available at http://www.eclipse.org/org/documents/edl-v10.php
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  21. * - Redistributions in binary form must reproduce the above
  22. * copyright notice, this list of conditions and the following
  23. * disclaimer in the documentation and/or other materials provided
  24. * with the distribution.
  25. *
  26. * - Neither the name of the Eclipse Foundation, Inc. nor the
  27. * names of its contributors may be used to endorse or promote
  28. * products derived from this software without specific prior
  29. * written permission.
  30. *
  31. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. package org.eclipse.jgit.transport;
  46. import java.io.EOFException;
  47. import java.io.File;
  48. import java.io.FileOutputStream;
  49. import java.io.IOException;
  50. import java.io.InputStream;
  51. import java.io.RandomAccessFile;
  52. import java.security.MessageDigest;
  53. import java.text.MessageFormat;
  54. import java.util.ArrayList;
  55. import java.util.Arrays;
  56. import java.util.List;
  57. import java.util.zip.CRC32;
  58. import java.util.zip.DataFormatException;
  59. import java.util.zip.Deflater;
  60. import java.util.zip.Inflater;
  61. import org.eclipse.jgit.JGitText;
  62. import org.eclipse.jgit.errors.CorruptObjectException;
  63. import org.eclipse.jgit.errors.MissingObjectException;
  64. import org.eclipse.jgit.lib.AnyObjectId;
  65. import org.eclipse.jgit.lib.Constants;
  66. import org.eclipse.jgit.lib.CoreConfig;
  67. import org.eclipse.jgit.lib.InflaterCache;
  68. import org.eclipse.jgit.lib.MutableObjectId;
  69. import org.eclipse.jgit.lib.ObjectChecker;
  70. import org.eclipse.jgit.lib.ObjectDatabase;
  71. import org.eclipse.jgit.lib.ObjectId;
  72. import org.eclipse.jgit.lib.ObjectIdSubclassMap;
  73. import org.eclipse.jgit.lib.ObjectLoader;
  74. import org.eclipse.jgit.lib.ObjectReader;
  75. import org.eclipse.jgit.lib.ProgressMonitor;
  76. import org.eclipse.jgit.lib.Repository;
  77. import org.eclipse.jgit.storage.file.PackIndexWriter;
  78. import org.eclipse.jgit.storage.file.PackLock;
  79. import org.eclipse.jgit.storage.pack.BinaryDelta;
  80. import org.eclipse.jgit.util.FileUtils;
  81. import org.eclipse.jgit.util.IO;
  82. import org.eclipse.jgit.util.NB;
  83. /** Indexes Git pack files for local use. */
  84. public class IndexPack {
  85. /**
  86. * Size of the internal stream buffer.
  87. * <p>
  88. * If callers are going to be supplying IndexPack a BufferedInputStream they
  89. * should use this buffer size as the size of the buffer for that
  90. * BufferedInputStream, and any other its may be wrapping. This way the
  91. * buffers will cascade efficiently and only the IndexPack buffer will be
  92. * receiving the bulk of the data stream.
  93. */
  94. public static final int BUFFER_SIZE = 8192;
  95. /**
  96. * Create an index pack instance to load a new pack into a repository.
  97. * <p>
  98. * The received pack data and generated index will be saved to temporary
  99. * files within the repository's <code>objects</code> directory. To use the
  100. * data contained within them call {@link #renameAndOpenPack()} once the
  101. * indexing is complete.
  102. *
  103. * @param db
  104. * the repository that will receive the new pack.
  105. * @param is
  106. * stream to read the pack data from. If the stream is buffered
  107. * use {@link #BUFFER_SIZE} as the buffer size for the stream.
  108. * @return a new index pack instance.
  109. * @throws IOException
  110. * a temporary file could not be created.
  111. */
  112. public static IndexPack create(final Repository db, final InputStream is)
  113. throws IOException {
  114. final String suffix = ".pack";
  115. final File objdir = db.getObjectsDirectory();
  116. final File tmp = File.createTempFile("incoming_", suffix, objdir);
  117. final String n = tmp.getName();
  118. final File base;
  119. base = new File(objdir, n.substring(0, n.length() - suffix.length()));
  120. final IndexPack ip = new IndexPack(db, is, base);
  121. ip.setIndexVersion(db.getConfig().get(CoreConfig.KEY)
  122. .getPackIndexVersion());
  123. return ip;
  124. }
  125. private static enum Source {
  126. /** Data is read from the incoming stream. */
  127. INPUT,
  128. /**
  129. * Data is read from the spooled pack file.
  130. * <p>
  131. * During streaming, some (or all) data might be saved into the spooled
  132. * pack file so it can be randomly accessed later.
  133. */
  134. FILE;
  135. }
  136. private final Repository repo;
  137. /**
  138. * Object database used for loading existing objects
  139. */
  140. private final ObjectDatabase objectDatabase;
  141. private InflaterStream inflater;
  142. private final MessageDigest objectDigest;
  143. private final MutableObjectId tempObjectId;
  144. private InputStream in;
  145. private byte[] buf;
  146. private long bBase;
  147. private int bOffset;
  148. private int bAvail;
  149. private ObjectChecker objCheck;
  150. private boolean fixThin;
  151. private boolean keepEmpty;
  152. private boolean needBaseObjectIds;
  153. private int outputVersion;
  154. private final File dstPack;
  155. private final File dstIdx;
  156. private long objectCount;
  157. private PackedObjectInfo[] entries;
  158. /**
  159. * Every object contained within the incoming pack.
  160. * <p>
  161. * This is a subset of {@link #entries}, as thin packs can add additional
  162. * objects to {@code entries} by copying already existing objects from the
  163. * repository onto the end of the thin pack to make it self-contained.
  164. */
  165. private ObjectIdSubclassMap<ObjectId> newObjectIds;
  166. private int deltaCount;
  167. private int entryCount;
  168. private final CRC32 crc = new CRC32();
  169. private ObjectIdSubclassMap<DeltaChain> baseById;
  170. /**
  171. * Objects referenced by their name from deltas, that aren't in this pack.
  172. * <p>
  173. * This is the set of objects that were copied onto the end of this pack to
  174. * make it complete. These objects were not transmitted by the remote peer,
  175. * but instead were assumed to already exist in the local repository.
  176. */
  177. private ObjectIdSubclassMap<ObjectId> baseObjectIds;
  178. private LongMap<UnresolvedDelta> baseByPos;
  179. private MessageDigest packDigest;
  180. private RandomAccessFile packOut;
  181. private byte[] packcsum;
  182. /** If {@link #fixThin} this is the last byte of the original checksum. */
  183. private long originalEOF;
  184. private ObjectReader readCurs;
  185. /**
  186. * Create a new pack indexer utility.
  187. *
  188. * @param db
  189. * @param src
  190. * stream to read the pack data from. If the stream is buffered
  191. * use {@link #BUFFER_SIZE} as the buffer size for the stream.
  192. * @param dstBase
  193. * @throws IOException
  194. * the output packfile could not be created.
  195. */
  196. public IndexPack(final Repository db, final InputStream src,
  197. final File dstBase) throws IOException {
  198. repo = db;
  199. objectDatabase = db.getObjectDatabase().newCachedDatabase();
  200. in = src;
  201. inflater = new InflaterStream();
  202. readCurs = objectDatabase.newReader();
  203. buf = new byte[BUFFER_SIZE];
  204. objectDigest = Constants.newMessageDigest();
  205. tempObjectId = new MutableObjectId();
  206. packDigest = Constants.newMessageDigest();
  207. if (dstBase != null) {
  208. final File dir = dstBase.getParentFile();
  209. final String nam = dstBase.getName();
  210. dstPack = new File(dir, nam + ".pack");
  211. dstIdx = new File(dir, nam + ".idx");
  212. packOut = new RandomAccessFile(dstPack, "rw");
  213. packOut.setLength(0);
  214. } else {
  215. dstPack = null;
  216. dstIdx = null;
  217. }
  218. }
  219. /**
  220. * Set the pack index file format version this instance will create.
  221. *
  222. * @param version
  223. * the version to write. The special version 0 designates the
  224. * oldest (most compatible) format available for the objects.
  225. * @see PackIndexWriter
  226. */
  227. public void setIndexVersion(final int version) {
  228. outputVersion = version;
  229. }
  230. /**
  231. * Configure this index pack instance to make a thin pack complete.
  232. * <p>
  233. * Thin packs are sometimes used during network transfers to allow a delta
  234. * to be sent without a base object. Such packs are not permitted on disk.
  235. * They can be fixed by copying the base object onto the end of the pack.
  236. *
  237. * @param fix
  238. * true to enable fixing a thin pack.
  239. */
  240. public void setFixThin(final boolean fix) {
  241. fixThin = fix;
  242. }
  243. /**
  244. * Configure this index pack instance to keep an empty pack.
  245. * <p>
  246. * By default an empty pack (a pack with no objects) is not kept, as doing
  247. * so is completely pointless. With no objects in the pack there is no data
  248. * stored by it, so the pack is unnecessary.
  249. *
  250. * @param empty true to enable keeping an empty pack.
  251. */
  252. public void setKeepEmpty(final boolean empty) {
  253. keepEmpty = empty;
  254. }
  255. /**
  256. * Configure this index pack instance to keep track of new objects.
  257. * <p>
  258. * By default an index pack doesn't save the new objects that were created
  259. * when it was instantiated. Setting this flag to {@code true} allows the
  260. * caller to use {@link #getNewObjectIds()} to retrieve that list.
  261. *
  262. * @param b {@code true} to enable keeping track of new objects.
  263. */
  264. public void setNeedNewObjectIds(boolean b) {
  265. if (b)
  266. newObjectIds = new ObjectIdSubclassMap<ObjectId>();
  267. else
  268. newObjectIds = null;
  269. }
  270. private boolean needNewObjectIds() {
  271. return newObjectIds != null;
  272. }
  273. /**
  274. * Configure this index pack instance to keep track of the objects assumed
  275. * for delta bases.
  276. * <p>
  277. * By default an index pack doesn't save the objects that were used as delta
  278. * bases. Setting this flag to {@code true} will allow the caller to
  279. * use {@link #getBaseObjectIds()} to retrieve that list.
  280. *
  281. * @param b {@code true} to enable keeping track of delta bases.
  282. */
  283. public void setNeedBaseObjectIds(boolean b) {
  284. this.needBaseObjectIds = b;
  285. }
  286. /** @return the new objects that were sent by the user */
  287. public ObjectIdSubclassMap<ObjectId> getNewObjectIds() {
  288. if (newObjectIds != null)
  289. return newObjectIds;
  290. return new ObjectIdSubclassMap<ObjectId>();
  291. }
  292. /** @return set of objects the incoming pack assumed for delta purposes */
  293. public ObjectIdSubclassMap<ObjectId> getBaseObjectIds() {
  294. if (baseObjectIds != null)
  295. return baseObjectIds;
  296. return new ObjectIdSubclassMap<ObjectId>();
  297. }
  298. /**
  299. * Configure the checker used to validate received objects.
  300. * <p>
  301. * Usually object checking isn't necessary, as Git implementations only
  302. * create valid objects in pack files. However, additional checking may be
  303. * useful if processing data from an untrusted source.
  304. *
  305. * @param oc
  306. * the checker instance; null to disable object checking.
  307. */
  308. public void setObjectChecker(final ObjectChecker oc) {
  309. objCheck = oc;
  310. }
  311. /**
  312. * Configure the checker used to validate received objects.
  313. * <p>
  314. * Usually object checking isn't necessary, as Git implementations only
  315. * create valid objects in pack files. However, additional checking may be
  316. * useful if processing data from an untrusted source.
  317. * <p>
  318. * This is shorthand for:
  319. *
  320. * <pre>
  321. * setObjectChecker(on ? new ObjectChecker() : null);
  322. * </pre>
  323. *
  324. * @param on
  325. * true to enable the default checker; false to disable it.
  326. */
  327. public void setObjectChecking(final boolean on) {
  328. setObjectChecker(on ? new ObjectChecker() : null);
  329. }
  330. /**
  331. * Consume data from the input stream until the packfile is indexed.
  332. *
  333. * @param progress
  334. * progress feedback
  335. *
  336. * @throws IOException
  337. */
  338. public void index(final ProgressMonitor progress) throws IOException {
  339. progress.start(2 /* tasks */);
  340. try {
  341. try {
  342. readPackHeader();
  343. entries = new PackedObjectInfo[(int) objectCount];
  344. baseById = new ObjectIdSubclassMap<DeltaChain>();
  345. baseByPos = new LongMap<UnresolvedDelta>();
  346. progress.beginTask(JGitText.get().receivingObjects,
  347. (int) objectCount);
  348. for (int done = 0; done < objectCount; done++) {
  349. indexOneObject();
  350. progress.update(1);
  351. if (progress.isCancelled())
  352. throw new IOException(JGitText.get().downloadCancelled);
  353. }
  354. readPackFooter();
  355. endInput();
  356. progress.endTask();
  357. if (deltaCount > 0) {
  358. if (packOut == null)
  359. throw new IOException(JGitText.get().needPackOut);
  360. resolveDeltas(progress);
  361. if (entryCount < objectCount) {
  362. if (!fixThin) {
  363. throw new IOException(MessageFormat.format(
  364. JGitText.get().packHasUnresolvedDeltas, (objectCount - entryCount)));
  365. }
  366. fixThinPack(progress);
  367. }
  368. }
  369. if (packOut != null && (keepEmpty || entryCount > 0))
  370. packOut.getChannel().force(true);
  371. packDigest = null;
  372. baseById = null;
  373. baseByPos = null;
  374. if (dstIdx != null && (keepEmpty || entryCount > 0))
  375. writeIdx();
  376. } finally {
  377. try {
  378. if (readCurs != null)
  379. readCurs.release();
  380. } finally {
  381. readCurs = null;
  382. }
  383. try {
  384. inflater.release();
  385. } finally {
  386. inflater = null;
  387. objectDatabase.close();
  388. }
  389. progress.endTask();
  390. if (packOut != null)
  391. packOut.close();
  392. }
  393. if (keepEmpty || entryCount > 0) {
  394. if (dstPack != null)
  395. dstPack.setReadOnly();
  396. if (dstIdx != null)
  397. dstIdx.setReadOnly();
  398. }
  399. } catch (IOException err) {
  400. if (dstPack != null)
  401. FileUtils.delete(dstPack);
  402. if (dstIdx != null)
  403. FileUtils.delete(dstIdx);
  404. throw err;
  405. }
  406. }
  407. private void resolveDeltas(final ProgressMonitor progress)
  408. throws IOException {
  409. progress.beginTask(JGitText.get().resolvingDeltas, deltaCount);
  410. final int last = entryCount;
  411. for (int i = 0; i < last; i++) {
  412. final int before = entryCount;
  413. resolveDeltas(entries[i]);
  414. progress.update(entryCount - before);
  415. if (progress.isCancelled())
  416. throw new IOException(JGitText.get().downloadCancelledDuringIndexing);
  417. }
  418. progress.endTask();
  419. }
  420. private void resolveDeltas(final PackedObjectInfo oe) throws IOException {
  421. final int oldCRC = oe.getCRC();
  422. if (baseById.get(oe) != null || baseByPos.containsKey(oe.getOffset()))
  423. resolveDeltas(oe.getOffset(), oldCRC, Constants.OBJ_BAD, null, oe);
  424. }
  425. private void resolveDeltas(final long pos, final int oldCRC, int type,
  426. byte[] data, PackedObjectInfo oe) throws IOException {
  427. crc.reset();
  428. position(pos);
  429. int c = readFrom(Source.FILE);
  430. final int typeCode = (c >> 4) & 7;
  431. long sz = c & 15;
  432. int shift = 4;
  433. while ((c & 0x80) != 0) {
  434. c = readFrom(Source.FILE);
  435. sz += (c & 0x7f) << shift;
  436. shift += 7;
  437. }
  438. switch (typeCode) {
  439. case Constants.OBJ_COMMIT:
  440. case Constants.OBJ_TREE:
  441. case Constants.OBJ_BLOB:
  442. case Constants.OBJ_TAG:
  443. type = typeCode;
  444. data = inflateAndReturn(Source.FILE, sz);
  445. break;
  446. case Constants.OBJ_OFS_DELTA: {
  447. c = readFrom(Source.FILE) & 0xff;
  448. while ((c & 128) != 0)
  449. c = readFrom(Source.FILE) & 0xff;
  450. data = BinaryDelta.apply(data, inflateAndReturn(Source.FILE, sz));
  451. break;
  452. }
  453. case Constants.OBJ_REF_DELTA: {
  454. crc.update(buf, fill(Source.FILE, 20), 20);
  455. use(20);
  456. data = BinaryDelta.apply(data, inflateAndReturn(Source.FILE, sz));
  457. break;
  458. }
  459. default:
  460. throw new IOException(MessageFormat.format(JGitText.get().unknownObjectType, typeCode));
  461. }
  462. final int crc32 = (int) crc.getValue();
  463. if (oldCRC != crc32)
  464. throw new IOException(MessageFormat.format(JGitText.get().corruptionDetectedReReadingAt, pos));
  465. if (oe == null) {
  466. objectDigest.update(Constants.encodedTypeString(type));
  467. objectDigest.update((byte) ' ');
  468. objectDigest.update(Constants.encodeASCII(data.length));
  469. objectDigest.update((byte) 0);
  470. objectDigest.update(data);
  471. tempObjectId.fromRaw(objectDigest.digest(), 0);
  472. verifySafeObject(tempObjectId, type, data);
  473. oe = new PackedObjectInfo(pos, crc32, tempObjectId);
  474. addObjectAndTrack(oe);
  475. }
  476. resolveChildDeltas(pos, type, data, oe);
  477. }
  478. private UnresolvedDelta removeBaseById(final AnyObjectId id){
  479. final DeltaChain d = baseById.get(id);
  480. return d != null ? d.remove() : null;
  481. }
  482. private static UnresolvedDelta reverse(UnresolvedDelta c) {
  483. UnresolvedDelta tail = null;
  484. while (c != null) {
  485. final UnresolvedDelta n = c.next;
  486. c.next = tail;
  487. tail = c;
  488. c = n;
  489. }
  490. return tail;
  491. }
  492. private void resolveChildDeltas(final long pos, int type, byte[] data,
  493. PackedObjectInfo oe) throws IOException {
  494. UnresolvedDelta a = reverse(removeBaseById(oe));
  495. UnresolvedDelta b = reverse(baseByPos.remove(pos));
  496. while (a != null && b != null) {
  497. if (a.position < b.position) {
  498. resolveDeltas(a.position, a.crc, type, data, null);
  499. a = a.next;
  500. } else {
  501. resolveDeltas(b.position, b.crc, type, data, null);
  502. b = b.next;
  503. }
  504. }
  505. resolveChildDeltaChain(type, data, a);
  506. resolveChildDeltaChain(type, data, b);
  507. }
  508. private void resolveChildDeltaChain(final int type, final byte[] data,
  509. UnresolvedDelta a) throws IOException {
  510. while (a != null) {
  511. resolveDeltas(a.position, a.crc, type, data, null);
  512. a = a.next;
  513. }
  514. }
  515. private void fixThinPack(final ProgressMonitor progress) throws IOException {
  516. growEntries();
  517. if (needBaseObjectIds)
  518. baseObjectIds = new ObjectIdSubclassMap<ObjectId>();
  519. packDigest.reset();
  520. originalEOF = packOut.length() - 20;
  521. final Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
  522. final List<DeltaChain> missing = new ArrayList<DeltaChain>(64);
  523. long end = originalEOF;
  524. for (final DeltaChain baseId : baseById) {
  525. if (baseId.head == null)
  526. continue;
  527. if (needBaseObjectIds)
  528. baseObjectIds.add(baseId);
  529. final ObjectLoader ldr;
  530. try {
  531. ldr = readCurs.open(baseId);
  532. } catch (MissingObjectException notFound) {
  533. missing.add(baseId);
  534. continue;
  535. }
  536. final byte[] data = ldr.getCachedBytes(Integer.MAX_VALUE);
  537. final int typeCode = ldr.getType();
  538. final PackedObjectInfo oe;
  539. crc.reset();
  540. packOut.seek(end);
  541. writeWhole(def, typeCode, data);
  542. oe = new PackedObjectInfo(end, (int) crc.getValue(), baseId);
  543. entries[entryCount++] = oe;
  544. end = packOut.getFilePointer();
  545. resolveChildDeltas(oe.getOffset(), typeCode, data, oe);
  546. if (progress.isCancelled())
  547. throw new IOException(JGitText.get().downloadCancelledDuringIndexing);
  548. }
  549. def.end();
  550. for (final DeltaChain base : missing) {
  551. if (base.head != null)
  552. throw new MissingObjectException(base, "delta base");
  553. }
  554. if (end - originalEOF < 20) {
  555. // Ugly corner case; if what we appended on to complete deltas
  556. // doesn't completely cover the SHA-1 we have to truncate off
  557. // we need to shorten the file, otherwise we will include part
  558. // of the old footer as object content.
  559. packOut.setLength(end);
  560. }
  561. fixHeaderFooter(packcsum, packDigest.digest());
  562. }
  563. private void writeWhole(final Deflater def, final int typeCode,
  564. final byte[] data) throws IOException {
  565. int sz = data.length;
  566. int hdrlen = 0;
  567. buf[hdrlen++] = (byte) ((typeCode << 4) | sz & 15);
  568. sz >>>= 4;
  569. while (sz > 0) {
  570. buf[hdrlen - 1] |= 0x80;
  571. buf[hdrlen++] = (byte) (sz & 0x7f);
  572. sz >>>= 7;
  573. }
  574. packDigest.update(buf, 0, hdrlen);
  575. crc.update(buf, 0, hdrlen);
  576. packOut.write(buf, 0, hdrlen);
  577. def.reset();
  578. def.setInput(data);
  579. def.finish();
  580. while (!def.finished()) {
  581. final int datlen = def.deflate(buf);
  582. packDigest.update(buf, 0, datlen);
  583. crc.update(buf, 0, datlen);
  584. packOut.write(buf, 0, datlen);
  585. }
  586. }
  587. private void fixHeaderFooter(final byte[] origcsum, final byte[] tailcsum)
  588. throws IOException {
  589. final MessageDigest origDigest = Constants.newMessageDigest();
  590. final MessageDigest tailDigest = Constants.newMessageDigest();
  591. long origRemaining = originalEOF;
  592. packOut.seek(0);
  593. bAvail = 0;
  594. bOffset = 0;
  595. fill(Source.FILE, 12);
  596. {
  597. final int origCnt = (int) Math.min(bAvail, origRemaining);
  598. origDigest.update(buf, 0, origCnt);
  599. origRemaining -= origCnt;
  600. if (origRemaining == 0)
  601. tailDigest.update(buf, origCnt, bAvail - origCnt);
  602. }
  603. NB.encodeInt32(buf, 8, entryCount);
  604. packOut.seek(0);
  605. packOut.write(buf, 0, 12);
  606. packOut.seek(bAvail);
  607. packDigest.reset();
  608. packDigest.update(buf, 0, bAvail);
  609. for (;;) {
  610. final int n = packOut.read(buf);
  611. if (n < 0)
  612. break;
  613. if (origRemaining != 0) {
  614. final int origCnt = (int) Math.min(n, origRemaining);
  615. origDigest.update(buf, 0, origCnt);
  616. origRemaining -= origCnt;
  617. if (origRemaining == 0)
  618. tailDigest.update(buf, origCnt, n - origCnt);
  619. } else
  620. tailDigest.update(buf, 0, n);
  621. packDigest.update(buf, 0, n);
  622. }
  623. if (!Arrays.equals(origDigest.digest(), origcsum)
  624. || !Arrays.equals(tailDigest.digest(), tailcsum))
  625. throw new IOException(JGitText.get().packCorruptedWhileWritingToFilesystem);
  626. packcsum = packDigest.digest();
  627. packOut.write(packcsum);
  628. }
  629. private void growEntries() {
  630. final PackedObjectInfo[] ne;
  631. ne = new PackedObjectInfo[(int) objectCount + baseById.size()];
  632. System.arraycopy(entries, 0, ne, 0, entryCount);
  633. entries = ne;
  634. }
  635. private void writeIdx() throws IOException {
  636. Arrays.sort(entries, 0, entryCount);
  637. List<PackedObjectInfo> list = Arrays.asList(entries);
  638. if (entryCount < entries.length)
  639. list = list.subList(0, entryCount);
  640. final FileOutputStream os = new FileOutputStream(dstIdx);
  641. try {
  642. final PackIndexWriter iw;
  643. if (outputVersion <= 0)
  644. iw = PackIndexWriter.createOldestPossible(os, list);
  645. else
  646. iw = PackIndexWriter.createVersion(os, outputVersion);
  647. iw.write(list, packcsum);
  648. os.getChannel().force(true);
  649. } finally {
  650. os.close();
  651. }
  652. }
  653. private void readPackHeader() throws IOException {
  654. final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
  655. final int p = fill(Source.INPUT, hdrln);
  656. for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
  657. if (buf[p + k] != Constants.PACK_SIGNATURE[k])
  658. throw new IOException(JGitText.get().notAPACKFile);
  659. final long vers = NB.decodeUInt32(buf, p + 4);
  660. if (vers != 2 && vers != 3)
  661. throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
  662. objectCount = NB.decodeUInt32(buf, p + 8);
  663. use(hdrln);
  664. }
  665. private void readPackFooter() throws IOException {
  666. sync();
  667. final byte[] cmpcsum = packDigest.digest();
  668. final int c = fill(Source.INPUT, 20);
  669. packcsum = new byte[20];
  670. System.arraycopy(buf, c, packcsum, 0, 20);
  671. use(20);
  672. if (packOut != null)
  673. packOut.write(packcsum);
  674. if (!Arrays.equals(cmpcsum, packcsum))
  675. throw new CorruptObjectException(JGitText.get().corruptObjectPackfileChecksumIncorrect);
  676. }
  677. // Cleanup all resources associated with our input parsing.
  678. private void endInput() {
  679. in = null;
  680. }
  681. // Read one entire object or delta from the input.
  682. private void indexOneObject() throws IOException {
  683. final long pos = position();
  684. crc.reset();
  685. int c = readFrom(Source.INPUT);
  686. final int typeCode = (c >> 4) & 7;
  687. long sz = c & 15;
  688. int shift = 4;
  689. while ((c & 0x80) != 0) {
  690. c = readFrom(Source.INPUT);
  691. sz += (c & 0x7f) << shift;
  692. shift += 7;
  693. }
  694. switch (typeCode) {
  695. case Constants.OBJ_COMMIT:
  696. case Constants.OBJ_TREE:
  697. case Constants.OBJ_BLOB:
  698. case Constants.OBJ_TAG:
  699. whole(typeCode, pos, sz);
  700. break;
  701. case Constants.OBJ_OFS_DELTA: {
  702. c = readFrom(Source.INPUT);
  703. long ofs = c & 127;
  704. while ((c & 128) != 0) {
  705. ofs += 1;
  706. c = readFrom(Source.INPUT);
  707. ofs <<= 7;
  708. ofs += (c & 127);
  709. }
  710. final long base = pos - ofs;
  711. final UnresolvedDelta n;
  712. inflateAndSkip(Source.INPUT, sz);
  713. n = new UnresolvedDelta(pos, (int) crc.getValue());
  714. n.next = baseByPos.put(base, n);
  715. deltaCount++;
  716. break;
  717. }
  718. case Constants.OBJ_REF_DELTA: {
  719. c = fill(Source.INPUT, 20);
  720. crc.update(buf, c, 20);
  721. final ObjectId base = ObjectId.fromRaw(buf, c);
  722. use(20);
  723. DeltaChain r = baseById.get(base);
  724. if (r == null) {
  725. r = new DeltaChain(base);
  726. baseById.add(r);
  727. }
  728. inflateAndSkip(Source.INPUT, sz);
  729. r.add(new UnresolvedDelta(pos, (int) crc.getValue()));
  730. deltaCount++;
  731. break;
  732. }
  733. default:
  734. throw new IOException(MessageFormat.format(JGitText.get().unknownObjectType, typeCode));
  735. }
  736. }
  737. private void whole(final int type, final long pos, final long sz)
  738. throws IOException {
  739. final byte[] data = inflateAndReturn(Source.INPUT, sz);
  740. objectDigest.update(Constants.encodedTypeString(type));
  741. objectDigest.update((byte) ' ');
  742. objectDigest.update(Constants.encodeASCII(sz));
  743. objectDigest.update((byte) 0);
  744. objectDigest.update(data);
  745. tempObjectId.fromRaw(objectDigest.digest(), 0);
  746. verifySafeObject(tempObjectId, type, data);
  747. final int crc32 = (int) crc.getValue();
  748. addObjectAndTrack(new PackedObjectInfo(pos, crc32, tempObjectId));
  749. }
  750. private void verifySafeObject(final AnyObjectId id, final int type,
  751. final byte[] data) throws IOException {
  752. if (objCheck != null) {
  753. try {
  754. objCheck.check(type, data);
  755. } catch (CorruptObjectException e) {
  756. throw new IOException(MessageFormat.format(JGitText.get().invalidObject
  757. , Constants.typeString(type) , id.name() , e.getMessage()));
  758. }
  759. }
  760. try {
  761. final ObjectLoader ldr = readCurs.open(id, type);
  762. final byte[] existingData = ldr.getCachedBytes(Integer.MAX_VALUE);
  763. if (!Arrays.equals(data, existingData)) {
  764. throw new IOException(MessageFormat.format(JGitText.get().collisionOn, id.name()));
  765. }
  766. } catch (MissingObjectException notLocal) {
  767. // This is OK, we don't have a copy of the object locally
  768. // but the API throws when we try to read it as usually its
  769. // an error to read something that doesn't exist.
  770. }
  771. }
  772. // Current position of {@link #bOffset} within the entire file.
  773. private long position() {
  774. return bBase + bOffset;
  775. }
  776. private void position(final long pos) throws IOException {
  777. packOut.seek(pos);
  778. bBase = pos;
  779. bOffset = 0;
  780. bAvail = 0;
  781. }
  782. // Consume exactly one byte from the buffer and return it.
  783. private int readFrom(final Source src) throws IOException {
  784. if (bAvail == 0)
  785. fill(src, 1);
  786. bAvail--;
  787. final int b = buf[bOffset++] & 0xff;
  788. crc.update(b);
  789. return b;
  790. }
  791. // Consume cnt bytes from the buffer.
  792. private void use(final int cnt) {
  793. bOffset += cnt;
  794. bAvail -= cnt;
  795. }
  796. // Ensure at least need bytes are available in in {@link #buf}.
  797. private int fill(final Source src, final int need) throws IOException {
  798. while (bAvail < need) {
  799. int next = bOffset + bAvail;
  800. int free = buf.length - next;
  801. if (free + bAvail < need) {
  802. switch(src){
  803. case INPUT:
  804. sync();
  805. break;
  806. case FILE:
  807. if (bAvail > 0)
  808. System.arraycopy(buf, bOffset, buf, 0, bAvail);
  809. bOffset = 0;
  810. break;
  811. }
  812. next = bAvail;
  813. free = buf.length - next;
  814. }
  815. switch(src){
  816. case INPUT:
  817. next = in.read(buf, next, free);
  818. break;
  819. case FILE:
  820. next = packOut.read(buf, next, free);
  821. break;
  822. }
  823. if (next <= 0)
  824. throw new EOFException(JGitText.get().packfileIsTruncated);
  825. bAvail += next;
  826. }
  827. return bOffset;
  828. }
  829. // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
  830. private void sync() throws IOException {
  831. packDigest.update(buf, 0, bOffset);
  832. if (packOut != null)
  833. packOut.write(buf, 0, bOffset);
  834. if (bAvail > 0)
  835. System.arraycopy(buf, bOffset, buf, 0, bAvail);
  836. bBase += bOffset;
  837. bOffset = 0;
  838. }
  839. private void inflateAndSkip(final Source src, final long inflatedSize)
  840. throws IOException {
  841. final InputStream inf = inflate(src, inflatedSize);
  842. IO.skipFully(inf, inflatedSize);
  843. inf.close();
  844. }
  845. private byte[] inflateAndReturn(final Source src, final long inflatedSize)
  846. throws IOException {
  847. final byte[] dst = new byte[(int) inflatedSize];
  848. final InputStream inf = inflate(src, inflatedSize);
  849. IO.readFully(inf, dst, 0, dst.length);
  850. inf.close();
  851. return dst;
  852. }
  853. private InputStream inflate(final Source src, final long inflatedSize)
  854. throws IOException {
  855. inflater.open(src, inflatedSize);
  856. return inflater;
  857. }
  858. private static class DeltaChain extends ObjectId {
  859. UnresolvedDelta head;
  860. DeltaChain(final AnyObjectId id) {
  861. super(id);
  862. }
  863. UnresolvedDelta remove() {
  864. final UnresolvedDelta r = head;
  865. if (r != null)
  866. head = null;
  867. return r;
  868. }
  869. void add(final UnresolvedDelta d) {
  870. d.next = head;
  871. head = d;
  872. }
  873. }
  874. private static class UnresolvedDelta {
  875. final long position;
  876. final int crc;
  877. UnresolvedDelta next;
  878. UnresolvedDelta(final long headerOffset, final int crc32) {
  879. position = headerOffset;
  880. crc = crc32;
  881. }
  882. }
  883. /**
  884. * Rename the pack to it's final name and location and open it.
  885. * <p>
  886. * If the call completes successfully the repository this IndexPack instance
  887. * was created with will have the objects in the pack available for reading
  888. * and use, without needing to scan for packs.
  889. *
  890. * @throws IOException
  891. * The pack could not be inserted into the repository's objects
  892. * directory. The pack no longer exists on disk, as it was
  893. * removed prior to throwing the exception to the caller.
  894. */
  895. public void renameAndOpenPack() throws IOException {
  896. renameAndOpenPack(null);
  897. }
  898. /**
  899. * Rename the pack to it's final name and location and open it.
  900. * <p>
  901. * If the call completes successfully the repository this IndexPack instance
  902. * was created with will have the objects in the pack available for reading
  903. * and use, without needing to scan for packs.
  904. *
  905. * @param lockMessage
  906. * message to place in the pack-*.keep file. If null, no lock
  907. * will be created, and this method returns null.
  908. * @return the pack lock object, if lockMessage is not null.
  909. * @throws IOException
  910. * The pack could not be inserted into the repository's objects
  911. * directory. The pack no longer exists on disk, as it was
  912. * removed prior to throwing the exception to the caller.
  913. */
  914. public PackLock renameAndOpenPack(final String lockMessage)
  915. throws IOException {
  916. if (!keepEmpty && entryCount == 0) {
  917. cleanupTemporaryFiles();
  918. return null;
  919. }
  920. final MessageDigest d = Constants.newMessageDigest();
  921. final byte[] oeBytes = new byte[Constants.OBJECT_ID_LENGTH];
  922. for (int i = 0; i < entryCount; i++) {
  923. final PackedObjectInfo oe = entries[i];
  924. oe.copyRawTo(oeBytes, 0);
  925. d.update(oeBytes);
  926. }
  927. final String name = ObjectId.fromRaw(d.digest()).name();
  928. final File packDir = new File(repo.getObjectsDirectory(), "pack");
  929. final File finalPack = new File(packDir, "pack-" + name + ".pack");
  930. final File finalIdx = new File(packDir, "pack-" + name + ".idx");
  931. final PackLock keep = new PackLock(finalPack, repo.getFS());
  932. if (!packDir.exists() && !packDir.mkdir() && !packDir.exists()) {
  933. // The objects/pack directory isn't present, and we are unable
  934. // to create it. There is no way to move this pack in.
  935. //
  936. cleanupTemporaryFiles();
  937. throw new IOException(MessageFormat.format(JGitText.get().cannotCreateDirectory, packDir.getAbsolutePath()));
  938. }
  939. if (finalPack.exists()) {
  940. // If the pack is already present we should never replace it.
  941. //
  942. cleanupTemporaryFiles();
  943. return null;
  944. }
  945. if (lockMessage != null) {
  946. // If we have a reason to create a keep file for this pack, do
  947. // so, or fail fast and don't put the pack in place.
  948. //
  949. try {
  950. if (!keep.lock(lockMessage))
  951. throw new IOException(MessageFormat.format(JGitText.get().cannotLockPackIn, finalPack));
  952. } catch (IOException e) {
  953. cleanupTemporaryFiles();
  954. throw e;
  955. }
  956. }
  957. if (!dstPack.renameTo(finalPack)) {
  958. cleanupTemporaryFiles();
  959. keep.unlock();
  960. throw new IOException(MessageFormat.format(JGitText.get().cannotMovePackTo, finalPack));
  961. }
  962. if (!dstIdx.renameTo(finalIdx)) {
  963. cleanupTemporaryFiles();
  964. keep.unlock();
  965. if (!finalPack.delete())
  966. finalPack.deleteOnExit();
  967. throw new IOException(MessageFormat.format(JGitText.get().cannotMoveIndexTo, finalIdx));
  968. }
  969. try {
  970. repo.openPack(finalPack, finalIdx);
  971. } catch (IOException err) {
  972. keep.unlock();
  973. FileUtils.delete(finalPack);
  974. FileUtils.delete(finalIdx);
  975. throw err;
  976. }
  977. return lockMessage != null ? keep : null;
  978. }
  979. private void cleanupTemporaryFiles() {
  980. if (!dstIdx.delete())
  981. dstIdx.deleteOnExit();
  982. if (!dstPack.delete())
  983. dstPack.deleteOnExit();
  984. }
  985. private void addObjectAndTrack(PackedObjectInfo oe) {
  986. entries[entryCount++] = oe;
  987. if (needNewObjectIds())
  988. newObjectIds.add(oe);
  989. }
  990. private class InflaterStream extends InputStream {
  991. private final Inflater inf;
  992. private final byte[] skipBuffer;
  993. private Source src;
  994. private long expectedSize;
  995. private long actualSize;
  996. private int p;
  997. InflaterStream() {
  998. inf = InflaterCache.get();
  999. skipBuffer = new byte[512];
  1000. }
  1001. void release() {
  1002. inf.reset();
  1003. InflaterCache.release(inf);
  1004. }
  1005. void open(Source source, long inflatedSize) throws IOException {
  1006. src = source;
  1007. expectedSize = inflatedSize;
  1008. actualSize = 0;
  1009. p = fill(src, 24);
  1010. inf.setInput(buf, p, bAvail);
  1011. }
  1012. @Override
  1013. public long skip(long toSkip) throws IOException {
  1014. long n = 0;
  1015. while (n < toSkip) {
  1016. final int cnt = (int) Math.min(skipBuffer.length, toSkip - n);
  1017. final int r = read(skipBuffer, 0, cnt);
  1018. if (r <= 0)
  1019. break;
  1020. n += r;
  1021. }
  1022. return n;
  1023. }
  1024. @Override
  1025. public int read() throws IOException {
  1026. int n = read(skipBuffer, 0, 1);
  1027. return n == 1 ? skipBuffer[0] & 0xff : -1;
  1028. }
  1029. @Override
  1030. public int read(byte[] dst, int pos, int cnt) throws IOException {
  1031. try {
  1032. int n = 0;
  1033. while (n < cnt) {
  1034. int r = inf.inflate(dst, pos + n, cnt - n);
  1035. if (r == 0) {
  1036. if (inf.finished())
  1037. break;
  1038. if (inf.needsInput()) {
  1039. crc.update(buf, p, bAvail);
  1040. use(bAvail);
  1041. p = fill(src, 24);
  1042. inf.setInput(buf, p, bAvail);
  1043. } else {
  1044. throw new CorruptObjectException(
  1045. MessageFormat
  1046. .format(
  1047. JGitText.get().packfileCorruptionDetected,
  1048. JGitText.get().unknownZlibError));
  1049. }
  1050. } else {
  1051. n += r;
  1052. }
  1053. }
  1054. actualSize += n;
  1055. return 0 < n ? n : -1;
  1056. } catch (DataFormatException dfe) {
  1057. throw new CorruptObjectException(MessageFormat.format(JGitText
  1058. .get().packfileCorruptionDetected, dfe.getMessage()));
  1059. }
  1060. }
  1061. @Override
  1062. public void close() throws IOException {
  1063. // We need to read here to enter the loop above and pump the
  1064. // trailing checksum into the Inflater. It should return -1 as the
  1065. // caller was supposed to consume all content.
  1066. //
  1067. if (read(skipBuffer) != -1 || actualSize != expectedSize) {
  1068. throw new CorruptObjectException(MessageFormat.format(JGitText
  1069. .get().packfileCorruptionDetected,
  1070. JGitText.get().wrongDecompressedLength));
  1071. }
  1072. int used = bAvail - inf.getRemaining();
  1073. if (0 < used) {
  1074. crc.update(buf, p, used);
  1075. use(used);
  1076. }
  1077. inf.reset();
  1078. }
  1079. }
  1080. }