Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

IndexPack.java 33KB

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