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.

PackFile.java 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2006-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.internal.storage.file;
  46. import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
  47. import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
  48. import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
  49. import java.io.EOFException;
  50. import java.io.File;
  51. import java.io.FileNotFoundException;
  52. import java.io.IOException;
  53. import java.io.InterruptedIOException;
  54. import java.io.RandomAccessFile;
  55. import java.nio.MappedByteBuffer;
  56. import java.nio.channels.FileChannel.MapMode;
  57. import java.nio.file.AccessDeniedException;
  58. import java.nio.file.NoSuchFileException;
  59. import java.text.MessageFormat;
  60. import java.util.Arrays;
  61. import java.util.Collections;
  62. import java.util.Comparator;
  63. import java.util.Iterator;
  64. import java.util.Set;
  65. import java.util.concurrent.atomic.AtomicInteger;
  66. import java.util.zip.CRC32;
  67. import java.util.zip.DataFormatException;
  68. import java.util.zip.Inflater;
  69. import org.eclipse.jgit.errors.CorruptObjectException;
  70. import org.eclipse.jgit.errors.LargeObjectException;
  71. import org.eclipse.jgit.errors.MissingObjectException;
  72. import org.eclipse.jgit.errors.NoPackSignatureException;
  73. import org.eclipse.jgit.errors.PackInvalidException;
  74. import org.eclipse.jgit.errors.PackMismatchException;
  75. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  76. import org.eclipse.jgit.errors.UnpackException;
  77. import org.eclipse.jgit.errors.UnsupportedPackIndexVersionException;
  78. import org.eclipse.jgit.errors.UnsupportedPackVersionException;
  79. import org.eclipse.jgit.internal.JGitText;
  80. import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
  81. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  82. import org.eclipse.jgit.internal.storage.pack.PackExt;
  83. import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
  84. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  85. import org.eclipse.jgit.lib.AnyObjectId;
  86. import org.eclipse.jgit.lib.Constants;
  87. import org.eclipse.jgit.lib.ObjectId;
  88. import org.eclipse.jgit.lib.ObjectLoader;
  89. import org.eclipse.jgit.util.LongList;
  90. import org.eclipse.jgit.util.NB;
  91. import org.eclipse.jgit.util.RawParseUtils;
  92. /**
  93. * A Git version 2 pack file representation. A pack file contains Git objects in
  94. * delta packed format yielding high compression of lots of object where some
  95. * objects are similar.
  96. */
  97. public class PackFile implements Iterable<PackIndex.MutableEntry> {
  98. /** Sorts PackFiles to be most recently created to least recently created. */
  99. public static final Comparator<PackFile> SORT = new Comparator<PackFile>() {
  100. @Override
  101. public int compare(final PackFile a, final PackFile b) {
  102. return b.packLastModified - a.packLastModified;
  103. }
  104. };
  105. private final File packFile;
  106. private final int extensions;
  107. private File keepFile;
  108. private volatile String packName;
  109. final int hash;
  110. private RandomAccessFile fd;
  111. /** Serializes reads performed against {@link #fd}. */
  112. private final Object readLock = new Object();
  113. long length;
  114. private int activeWindows;
  115. private int activeCopyRawData;
  116. int packLastModified;
  117. private FileSnapshot fileSnapshot;
  118. private volatile boolean invalid;
  119. private volatile Exception invalidatingCause;
  120. private boolean invalidBitmap;
  121. private AtomicInteger transientErrorCount = new AtomicInteger();
  122. private byte[] packChecksum;
  123. private volatile PackIndex loadedIdx;
  124. private PackReverseIndex reverseIdx;
  125. private PackBitmapIndex bitmapIdx;
  126. /**
  127. * Objects we have tried to read, and discovered to be corrupt.
  128. * <p>
  129. * The list is allocated after the first corruption is found, and filled in
  130. * as more entries are discovered. Typically this list is never used, as
  131. * pack files do not usually contain corrupt objects.
  132. */
  133. private volatile LongList corruptObjects;
  134. /**
  135. * Construct a reader for an existing, pre-indexed packfile.
  136. *
  137. * @param packFile
  138. * path of the <code>.pack</code> file holding the data.
  139. * @param extensions
  140. * additional pack file extensions with the same base as the pack
  141. */
  142. public PackFile(final File packFile, int extensions) {
  143. this.packFile = packFile;
  144. this.fileSnapshot = FileSnapshot.save(packFile);
  145. this.packLastModified = (int) (fileSnapshot.lastModified() >> 10);
  146. this.extensions = extensions;
  147. // Multiply by 31 here so we can more directly combine with another
  148. // value in WindowCache.hash(), without doing the multiply there.
  149. //
  150. hash = System.identityHashCode(this) * 31;
  151. length = Long.MAX_VALUE;
  152. }
  153. private PackIndex idx() throws IOException {
  154. PackIndex idx = loadedIdx;
  155. if (idx == null) {
  156. synchronized (this) {
  157. idx = loadedIdx;
  158. if (idx == null) {
  159. if (invalid) {
  160. throw new PackInvalidException(packFile, invalidatingCause);
  161. }
  162. try {
  163. idx = PackIndex.open(extFile(INDEX));
  164. if (packChecksum == null) {
  165. packChecksum = idx.packChecksum;
  166. } else if (!Arrays.equals(packChecksum,
  167. idx.packChecksum)) {
  168. throw new PackMismatchException(MessageFormat
  169. .format(JGitText.get().packChecksumMismatch,
  170. packFile.getPath(),
  171. ObjectId.fromRaw(packChecksum)
  172. .name(),
  173. ObjectId.fromRaw(idx.packChecksum)
  174. .name()));
  175. }
  176. loadedIdx = idx;
  177. } catch (InterruptedIOException e) {
  178. // don't invalidate the pack, we are interrupted from
  179. // another thread
  180. throw e;
  181. } catch (IOException e) {
  182. invalid = true;
  183. invalidatingCause = e;
  184. throw e;
  185. }
  186. }
  187. }
  188. }
  189. return idx;
  190. }
  191. /** @return the File object which locates this pack on disk. */
  192. public File getPackFile() {
  193. return packFile;
  194. }
  195. /**
  196. * @return the index for this pack file.
  197. * @throws IOException
  198. */
  199. public PackIndex getIndex() throws IOException {
  200. return idx();
  201. }
  202. /** @return name extracted from {@code pack-*.pack} pattern. */
  203. public String getPackName() {
  204. String name = packName;
  205. if (name == null) {
  206. name = getPackFile().getName();
  207. if (name.startsWith("pack-")) //$NON-NLS-1$
  208. name = name.substring("pack-".length()); //$NON-NLS-1$
  209. if (name.endsWith(".pack")) //$NON-NLS-1$
  210. name = name.substring(0, name.length() - ".pack".length()); //$NON-NLS-1$
  211. packName = name;
  212. }
  213. return name;
  214. }
  215. /**
  216. * Determine if an object is contained within the pack file.
  217. * <p>
  218. * For performance reasons only the index file is searched; the main pack
  219. * content is ignored entirely.
  220. * </p>
  221. *
  222. * @param id
  223. * the object to look for. Must not be null.
  224. * @return true if the object is in this pack; false otherwise.
  225. * @throws IOException
  226. * the index file cannot be loaded into memory.
  227. */
  228. public boolean hasObject(final AnyObjectId id) throws IOException {
  229. final long offset = idx().findOffset(id);
  230. return 0 < offset && !isCorrupt(offset);
  231. }
  232. /**
  233. * Determines whether a .keep file exists for this pack file.
  234. *
  235. * @return true if a .keep file exist.
  236. */
  237. public boolean shouldBeKept() {
  238. if (keepFile == null)
  239. keepFile = extFile(KEEP);
  240. return keepFile.exists();
  241. }
  242. /**
  243. * Get an object from this pack.
  244. *
  245. * @param curs
  246. * temporary working space associated with the calling thread.
  247. * @param id
  248. * the object to obtain from the pack. Must not be null.
  249. * @return the object loader for the requested object if it is contained in
  250. * this pack; null if the object was not found.
  251. * @throws IOException
  252. * the pack file or the index could not be read.
  253. */
  254. ObjectLoader get(final WindowCursor curs, final AnyObjectId id)
  255. throws IOException {
  256. final long offset = idx().findOffset(id);
  257. return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
  258. }
  259. void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
  260. throws IOException {
  261. idx().resolve(matches, id, matchLimit);
  262. }
  263. /**
  264. * Close the resources utilized by this repository
  265. */
  266. public void close() {
  267. WindowCache.purge(this);
  268. synchronized (this) {
  269. loadedIdx = null;
  270. reverseIdx = null;
  271. }
  272. }
  273. /**
  274. * Provide iterator over entries in associated pack index, that should also
  275. * exist in this pack file. Objects returned by such iterator are mutable
  276. * during iteration.
  277. * <p>
  278. * Iterator returns objects in SHA-1 lexicographical order.
  279. * </p>
  280. *
  281. * @return iterator over entries of associated pack index
  282. *
  283. * @see PackIndex#iterator()
  284. */
  285. @Override
  286. public Iterator<PackIndex.MutableEntry> iterator() {
  287. try {
  288. return idx().iterator();
  289. } catch (IOException e) {
  290. return Collections.<PackIndex.MutableEntry> emptyList().iterator();
  291. }
  292. }
  293. /**
  294. * Obtain the total number of objects available in this pack. This method
  295. * relies on pack index, giving number of effectively available objects.
  296. *
  297. * @return number of objects in index of this pack, likewise in this pack
  298. * @throws IOException
  299. * the index file cannot be loaded into memory.
  300. */
  301. long getObjectCount() throws IOException {
  302. return idx().getObjectCount();
  303. }
  304. /**
  305. * Search for object id with the specified start offset in associated pack
  306. * (reverse) index.
  307. *
  308. * @param offset
  309. * start offset of object to find
  310. * @return object id for this offset, or null if no object was found
  311. * @throws IOException
  312. * the index file cannot be loaded into memory.
  313. */
  314. ObjectId findObjectForOffset(final long offset) throws IOException {
  315. return getReverseIdx().findObject(offset);
  316. }
  317. /**
  318. * Return the @{@link FileSnapshot} associated to the underlying packfile
  319. * that has been used when the object was created.
  320. *
  321. * @return the packfile @{@link FileSnapshot} that the object is loaded from.
  322. */
  323. FileSnapshot getFileSnapshot() {
  324. return fileSnapshot;
  325. }
  326. private final byte[] decompress(final long position, final int sz,
  327. final WindowCursor curs) throws IOException, DataFormatException {
  328. byte[] dstbuf;
  329. try {
  330. dstbuf = new byte[sz];
  331. } catch (OutOfMemoryError noMemory) {
  332. // The size may be larger than our heap allows, return null to
  333. // let the caller know allocation isn't possible and it should
  334. // use the large object streaming approach instead.
  335. //
  336. // For example, this can occur when sz is 640 MB, and JRE
  337. // maximum heap size is only 256 MB. Even if the JRE has
  338. // 200 MB free, it cannot allocate a 640 MB byte array.
  339. return null;
  340. }
  341. if (curs.inflate(this, position, dstbuf, false) != sz)
  342. throw new EOFException(MessageFormat.format(
  343. JGitText.get().shortCompressedStreamAt,
  344. Long.valueOf(position)));
  345. return dstbuf;
  346. }
  347. void copyPackAsIs(PackOutputStream out, WindowCursor curs)
  348. throws IOException {
  349. // Pin the first window, this ensures the length is accurate.
  350. curs.pin(this, 0);
  351. curs.copyPackAsIs(this, length, out);
  352. }
  353. final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
  354. boolean validate, WindowCursor curs) throws IOException,
  355. StoredObjectRepresentationNotAvailableException {
  356. beginCopyAsIs(src);
  357. try {
  358. copyAsIs2(out, src, validate, curs);
  359. } finally {
  360. endCopyAsIs();
  361. }
  362. }
  363. private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
  364. boolean validate, WindowCursor curs) throws IOException,
  365. StoredObjectRepresentationNotAvailableException {
  366. final CRC32 crc1 = validate ? new CRC32() : null;
  367. final CRC32 crc2 = validate ? new CRC32() : null;
  368. final byte[] buf = out.getCopyBuffer();
  369. // Rip apart the header so we can discover the size.
  370. //
  371. readFully(src.offset, buf, 0, 20, curs);
  372. int c = buf[0] & 0xff;
  373. final int typeCode = (c >> 4) & 7;
  374. long inflatedLength = c & 15;
  375. int shift = 4;
  376. int headerCnt = 1;
  377. while ((c & 0x80) != 0) {
  378. c = buf[headerCnt++] & 0xff;
  379. inflatedLength += ((long) (c & 0x7f)) << shift;
  380. shift += 7;
  381. }
  382. if (typeCode == Constants.OBJ_OFS_DELTA) {
  383. do {
  384. c = buf[headerCnt++] & 0xff;
  385. } while ((c & 128) != 0);
  386. if (validate) {
  387. assert(crc1 != null && crc2 != null);
  388. crc1.update(buf, 0, headerCnt);
  389. crc2.update(buf, 0, headerCnt);
  390. }
  391. } else if (typeCode == Constants.OBJ_REF_DELTA) {
  392. if (validate) {
  393. assert(crc1 != null && crc2 != null);
  394. crc1.update(buf, 0, headerCnt);
  395. crc2.update(buf, 0, headerCnt);
  396. }
  397. readFully(src.offset + headerCnt, buf, 0, 20, curs);
  398. if (validate) {
  399. assert(crc1 != null && crc2 != null);
  400. crc1.update(buf, 0, 20);
  401. crc2.update(buf, 0, 20);
  402. }
  403. headerCnt += 20;
  404. } else if (validate) {
  405. assert(crc1 != null && crc2 != null);
  406. crc1.update(buf, 0, headerCnt);
  407. crc2.update(buf, 0, headerCnt);
  408. }
  409. final long dataOffset = src.offset + headerCnt;
  410. final long dataLength = src.length;
  411. final long expectedCRC;
  412. final ByteArrayWindow quickCopy;
  413. // Verify the object isn't corrupt before sending. If it is,
  414. // we report it missing instead.
  415. //
  416. try {
  417. quickCopy = curs.quickCopy(this, dataOffset, dataLength);
  418. if (validate && idx().hasCRC32Support()) {
  419. assert(crc1 != null);
  420. // Index has the CRC32 code cached, validate the object.
  421. //
  422. expectedCRC = idx().findCRC32(src);
  423. if (quickCopy != null) {
  424. quickCopy.crc32(crc1, dataOffset, (int) dataLength);
  425. } else {
  426. long pos = dataOffset;
  427. long cnt = dataLength;
  428. while (cnt > 0) {
  429. final int n = (int) Math.min(cnt, buf.length);
  430. readFully(pos, buf, 0, n, curs);
  431. crc1.update(buf, 0, n);
  432. pos += n;
  433. cnt -= n;
  434. }
  435. }
  436. if (crc1.getValue() != expectedCRC) {
  437. setCorrupt(src.offset);
  438. throw new CorruptObjectException(MessageFormat.format(
  439. JGitText.get().objectAtHasBadZlibStream,
  440. Long.valueOf(src.offset), getPackFile()));
  441. }
  442. } else if (validate) {
  443. // We don't have a CRC32 code in the index, so compute it
  444. // now while inflating the raw data to get zlib to tell us
  445. // whether or not the data is safe.
  446. //
  447. Inflater inf = curs.inflater();
  448. byte[] tmp = new byte[1024];
  449. if (quickCopy != null) {
  450. quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
  451. } else {
  452. assert(crc1 != null);
  453. long pos = dataOffset;
  454. long cnt = dataLength;
  455. while (cnt > 0) {
  456. final int n = (int) Math.min(cnt, buf.length);
  457. readFully(pos, buf, 0, n, curs);
  458. crc1.update(buf, 0, n);
  459. inf.setInput(buf, 0, n);
  460. while (inf.inflate(tmp, 0, tmp.length) > 0)
  461. continue;
  462. pos += n;
  463. cnt -= n;
  464. }
  465. }
  466. if (!inf.finished() || inf.getBytesRead() != dataLength) {
  467. setCorrupt(src.offset);
  468. throw new EOFException(MessageFormat.format(
  469. JGitText.get().shortCompressedStreamAt,
  470. Long.valueOf(src.offset)));
  471. }
  472. assert(crc1 != null);
  473. expectedCRC = crc1.getValue();
  474. } else {
  475. expectedCRC = -1;
  476. }
  477. } catch (DataFormatException dataFormat) {
  478. setCorrupt(src.offset);
  479. CorruptObjectException corruptObject = new CorruptObjectException(
  480. MessageFormat.format(
  481. JGitText.get().objectAtHasBadZlibStream,
  482. Long.valueOf(src.offset), getPackFile()));
  483. corruptObject.initCause(dataFormat);
  484. StoredObjectRepresentationNotAvailableException gone;
  485. gone = new StoredObjectRepresentationNotAvailableException(src);
  486. gone.initCause(corruptObject);
  487. throw gone;
  488. } catch (IOException ioError) {
  489. StoredObjectRepresentationNotAvailableException gone;
  490. gone = new StoredObjectRepresentationNotAvailableException(src);
  491. gone.initCause(ioError);
  492. throw gone;
  493. }
  494. if (quickCopy != null) {
  495. // The entire object fits into a single byte array window slice,
  496. // and we have it pinned. Write this out without copying.
  497. //
  498. out.writeHeader(src, inflatedLength);
  499. quickCopy.write(out, dataOffset, (int) dataLength);
  500. } else if (dataLength <= buf.length) {
  501. // Tiny optimization: Lots of objects are very small deltas or
  502. // deflated commits that are likely to fit in the copy buffer.
  503. //
  504. if (!validate) {
  505. long pos = dataOffset;
  506. long cnt = dataLength;
  507. while (cnt > 0) {
  508. final int n = (int) Math.min(cnt, buf.length);
  509. readFully(pos, buf, 0, n, curs);
  510. pos += n;
  511. cnt -= n;
  512. }
  513. }
  514. out.writeHeader(src, inflatedLength);
  515. out.write(buf, 0, (int) dataLength);
  516. } else {
  517. // Now we are committed to sending the object. As we spool it out,
  518. // check its CRC32 code to make sure there wasn't corruption between
  519. // the verification we did above, and us actually outputting it.
  520. //
  521. out.writeHeader(src, inflatedLength);
  522. long pos = dataOffset;
  523. long cnt = dataLength;
  524. while (cnt > 0) {
  525. final int n = (int) Math.min(cnt, buf.length);
  526. readFully(pos, buf, 0, n, curs);
  527. if (validate) {
  528. assert(crc2 != null);
  529. crc2.update(buf, 0, n);
  530. }
  531. out.write(buf, 0, n);
  532. pos += n;
  533. cnt -= n;
  534. }
  535. if (validate) {
  536. assert(crc2 != null);
  537. if (crc2.getValue() != expectedCRC) {
  538. throw new CorruptObjectException(MessageFormat.format(
  539. JGitText.get().objectAtHasBadZlibStream,
  540. Long.valueOf(src.offset), getPackFile()));
  541. }
  542. }
  543. }
  544. }
  545. boolean invalid() {
  546. return invalid;
  547. }
  548. void setInvalid() {
  549. invalid = true;
  550. }
  551. int incrementTransientErrorCount() {
  552. return transientErrorCount.incrementAndGet();
  553. }
  554. void resetTransientErrorCount() {
  555. transientErrorCount.set(0);
  556. }
  557. private void readFully(final long position, final byte[] dstbuf,
  558. int dstoff, final int cnt, final WindowCursor curs)
  559. throws IOException {
  560. if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
  561. throw new EOFException();
  562. }
  563. private synchronized void beginCopyAsIs(ObjectToPack otp)
  564. throws StoredObjectRepresentationNotAvailableException {
  565. if (++activeCopyRawData == 1 && activeWindows == 0) {
  566. try {
  567. doOpen();
  568. } catch (IOException thisPackNotValid) {
  569. StoredObjectRepresentationNotAvailableException gone;
  570. gone = new StoredObjectRepresentationNotAvailableException(otp);
  571. gone.initCause(thisPackNotValid);
  572. throw gone;
  573. }
  574. }
  575. }
  576. private synchronized void endCopyAsIs() {
  577. if (--activeCopyRawData == 0 && activeWindows == 0)
  578. doClose();
  579. }
  580. synchronized boolean beginWindowCache() throws IOException {
  581. if (++activeWindows == 1) {
  582. if (activeCopyRawData == 0)
  583. doOpen();
  584. return true;
  585. }
  586. return false;
  587. }
  588. synchronized boolean endWindowCache() {
  589. final boolean r = --activeWindows == 0;
  590. if (r && activeCopyRawData == 0)
  591. doClose();
  592. return r;
  593. }
  594. private void doOpen() throws IOException {
  595. if (invalid) {
  596. throw new PackInvalidException(packFile, invalidatingCause);
  597. }
  598. try {
  599. synchronized (readLock) {
  600. fd = new RandomAccessFile(packFile, "r"); //$NON-NLS-1$
  601. length = fd.length();
  602. onOpenPack();
  603. }
  604. } catch (InterruptedIOException e) {
  605. // don't invalidate the pack, we are interrupted from another thread
  606. openFail(false, e);
  607. throw e;
  608. } catch (FileNotFoundException fn) {
  609. // don't invalidate the pack if opening an existing file failed
  610. // since it may be related to a temporary lack of resources (e.g.
  611. // max open files)
  612. openFail(!packFile.exists(), fn);
  613. throw fn;
  614. } catch (EOFException | AccessDeniedException | NoSuchFileException
  615. | CorruptObjectException | NoPackSignatureException
  616. | PackMismatchException | UnpackException
  617. | UnsupportedPackIndexVersionException
  618. | UnsupportedPackVersionException pe) {
  619. // exceptions signaling permanent problems with a pack
  620. openFail(true, pe);
  621. throw pe;
  622. } catch (IOException | RuntimeException ge) {
  623. // generic exceptions could be transient so we should not mark the
  624. // pack invalid to avoid false MissingObjectExceptions
  625. openFail(false, ge);
  626. throw ge;
  627. }
  628. }
  629. private void openFail(boolean invalidate, Exception cause) {
  630. activeWindows = 0;
  631. activeCopyRawData = 0;
  632. invalid = invalidate;
  633. invalidatingCause = cause;
  634. doClose();
  635. }
  636. private void doClose() {
  637. synchronized (readLock) {
  638. if (fd != null) {
  639. try {
  640. fd.close();
  641. } catch (IOException err) {
  642. // Ignore a close event. We had it open only for reading.
  643. // There should not be errors related to network buffers
  644. // not flushed, etc.
  645. }
  646. fd = null;
  647. }
  648. }
  649. }
  650. ByteArrayWindow read(final long pos, int size) throws IOException {
  651. synchronized (readLock) {
  652. if (invalid || fd == null) {
  653. // Due to concurrency between a read and another packfile invalidation thread
  654. // one thread could come up to this point and then fail with NPE.
  655. // Detect the situation and throw a proper exception so that can be properly
  656. // managed by the main packfile search loop and the Git client won't receive
  657. // any failures.
  658. throw new PackInvalidException(packFile, invalidatingCause);
  659. }
  660. if (length < pos + size)
  661. size = (int) (length - pos);
  662. final byte[] buf = new byte[size];
  663. fd.seek(pos);
  664. fd.readFully(buf, 0, size);
  665. return new ByteArrayWindow(this, pos, buf);
  666. }
  667. }
  668. ByteWindow mmap(final long pos, int size) throws IOException {
  669. synchronized (readLock) {
  670. if (length < pos + size)
  671. size = (int) (length - pos);
  672. MappedByteBuffer map;
  673. try {
  674. map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  675. } catch (IOException ioe1) {
  676. // The most likely reason this failed is the JVM has run out
  677. // of virtual memory. We need to discard quickly, and try to
  678. // force the GC to finalize and release any existing mappings.
  679. //
  680. System.gc();
  681. System.runFinalization();
  682. map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  683. }
  684. if (map.hasArray())
  685. return new ByteArrayWindow(this, pos, map.array());
  686. return new ByteBufferWindow(this, pos, map);
  687. }
  688. }
  689. private void onOpenPack() throws IOException {
  690. final PackIndex idx = idx();
  691. final byte[] buf = new byte[20];
  692. fd.seek(0);
  693. fd.readFully(buf, 0, 12);
  694. if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4) {
  695. throw new NoPackSignatureException(JGitText.get().notAPACKFile);
  696. }
  697. final long vers = NB.decodeUInt32(buf, 4);
  698. final long packCnt = NB.decodeUInt32(buf, 8);
  699. if (vers != 2 && vers != 3) {
  700. throw new UnsupportedPackVersionException(vers);
  701. }
  702. if (packCnt != idx.getObjectCount()) {
  703. throw new PackMismatchException(MessageFormat.format(
  704. JGitText.get().packObjectCountMismatch,
  705. Long.valueOf(packCnt), Long.valueOf(idx.getObjectCount()),
  706. getPackFile()));
  707. }
  708. fd.seek(length - 20);
  709. fd.readFully(buf, 0, 20);
  710. if (!Arrays.equals(buf, packChecksum)) {
  711. throw new PackMismatchException(MessageFormat.format(
  712. JGitText.get().packChecksumMismatch,
  713. getPackFile(),
  714. ObjectId.fromRaw(buf).name(),
  715. ObjectId.fromRaw(idx.packChecksum).name()));
  716. }
  717. }
  718. ObjectLoader load(final WindowCursor curs, long pos)
  719. throws IOException, LargeObjectException {
  720. try {
  721. final byte[] ib = curs.tempId;
  722. Delta delta = null;
  723. byte[] data = null;
  724. int type = Constants.OBJ_BAD;
  725. boolean cached = false;
  726. SEARCH: for (;;) {
  727. readFully(pos, ib, 0, 20, curs);
  728. int c = ib[0] & 0xff;
  729. final int typeCode = (c >> 4) & 7;
  730. long sz = c & 15;
  731. int shift = 4;
  732. int p = 1;
  733. while ((c & 0x80) != 0) {
  734. c = ib[p++] & 0xff;
  735. sz += ((long) (c & 0x7f)) << shift;
  736. shift += 7;
  737. }
  738. switch (typeCode) {
  739. case Constants.OBJ_COMMIT:
  740. case Constants.OBJ_TREE:
  741. case Constants.OBJ_BLOB:
  742. case Constants.OBJ_TAG: {
  743. if (delta != null || sz < curs.getStreamFileThreshold())
  744. data = decompress(pos + p, (int) sz, curs);
  745. if (delta != null) {
  746. type = typeCode;
  747. break SEARCH;
  748. }
  749. if (data != null)
  750. return new ObjectLoader.SmallObject(typeCode, data);
  751. else
  752. return new LargePackedWholeObject(typeCode, sz, pos, p,
  753. this, curs.db);
  754. }
  755. case Constants.OBJ_OFS_DELTA: {
  756. c = ib[p++] & 0xff;
  757. long base = c & 127;
  758. while ((c & 128) != 0) {
  759. base += 1;
  760. c = ib[p++] & 0xff;
  761. base <<= 7;
  762. base += (c & 127);
  763. }
  764. base = pos - base;
  765. delta = new Delta(delta, pos, (int) sz, p, base);
  766. if (sz != delta.deltaSize)
  767. break SEARCH;
  768. DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
  769. if (e != null) {
  770. type = e.type;
  771. data = e.data;
  772. cached = true;
  773. break SEARCH;
  774. }
  775. pos = base;
  776. continue SEARCH;
  777. }
  778. case Constants.OBJ_REF_DELTA: {
  779. readFully(pos + p, ib, 0, 20, curs);
  780. long base = findDeltaBase(ObjectId.fromRaw(ib));
  781. delta = new Delta(delta, pos, (int) sz, p + 20, base);
  782. if (sz != delta.deltaSize)
  783. break SEARCH;
  784. DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
  785. if (e != null) {
  786. type = e.type;
  787. data = e.data;
  788. cached = true;
  789. break SEARCH;
  790. }
  791. pos = base;
  792. continue SEARCH;
  793. }
  794. default:
  795. throw new IOException(MessageFormat.format(
  796. JGitText.get().unknownObjectType,
  797. Integer.valueOf(typeCode)));
  798. }
  799. }
  800. // At this point there is at least one delta to apply to data.
  801. // (Whole objects with no deltas to apply return early above.)
  802. if (data == null)
  803. throw new IOException(JGitText.get().inMemoryBufferLimitExceeded);
  804. assert(delta != null);
  805. do {
  806. // Cache only the base immediately before desired object.
  807. if (cached)
  808. cached = false;
  809. else if (delta.next == null)
  810. curs.getDeltaBaseCache().store(this, delta.basePos, data, type);
  811. pos = delta.deltaPos;
  812. final byte[] cmds = decompress(pos + delta.hdrLen,
  813. delta.deltaSize, curs);
  814. if (cmds == null) {
  815. data = null; // Discard base in case of OutOfMemoryError
  816. throw new LargeObjectException.OutOfMemory(new OutOfMemoryError());
  817. }
  818. final long sz = BinaryDelta.getResultSize(cmds);
  819. if (Integer.MAX_VALUE <= sz)
  820. throw new LargeObjectException.ExceedsByteArrayLimit();
  821. final byte[] result;
  822. try {
  823. result = new byte[(int) sz];
  824. } catch (OutOfMemoryError tooBig) {
  825. data = null; // Discard base in case of OutOfMemoryError
  826. throw new LargeObjectException.OutOfMemory(tooBig);
  827. }
  828. BinaryDelta.apply(data, cmds, result);
  829. data = result;
  830. delta = delta.next;
  831. } while (delta != null);
  832. return new ObjectLoader.SmallObject(type, data);
  833. } catch (DataFormatException dfe) {
  834. CorruptObjectException coe = new CorruptObjectException(
  835. MessageFormat.format(
  836. JGitText.get().objectAtHasBadZlibStream,
  837. Long.valueOf(pos), getPackFile()));
  838. coe.initCause(dfe);
  839. throw coe;
  840. }
  841. }
  842. private long findDeltaBase(ObjectId baseId) throws IOException,
  843. MissingObjectException {
  844. long ofs = idx().findOffset(baseId);
  845. if (ofs < 0)
  846. throw new MissingObjectException(baseId,
  847. JGitText.get().missingDeltaBase);
  848. return ofs;
  849. }
  850. private static class Delta {
  851. /** Child that applies onto this object. */
  852. final Delta next;
  853. /** Offset of the delta object. */
  854. final long deltaPos;
  855. /** Size of the inflated delta stream. */
  856. final int deltaSize;
  857. /** Total size of the delta's pack entry header (including base). */
  858. final int hdrLen;
  859. /** Offset of the base object this delta applies onto. */
  860. final long basePos;
  861. Delta(Delta next, long ofs, int sz, int hdrLen, long baseOffset) {
  862. this.next = next;
  863. this.deltaPos = ofs;
  864. this.deltaSize = sz;
  865. this.hdrLen = hdrLen;
  866. this.basePos = baseOffset;
  867. }
  868. }
  869. byte[] getDeltaHeader(WindowCursor wc, long pos)
  870. throws IOException, DataFormatException {
  871. // The delta stream starts as two variable length integers. If we
  872. // assume they are 64 bits each, we need 16 bytes to encode them,
  873. // plus 2 extra bytes for the variable length overhead. So 18 is
  874. // the longest delta instruction header.
  875. //
  876. final byte[] hdr = new byte[18];
  877. wc.inflate(this, pos, hdr, true /* headerOnly */);
  878. return hdr;
  879. }
  880. int getObjectType(final WindowCursor curs, long pos) throws IOException {
  881. final byte[] ib = curs.tempId;
  882. for (;;) {
  883. readFully(pos, ib, 0, 20, curs);
  884. int c = ib[0] & 0xff;
  885. final int type = (c >> 4) & 7;
  886. switch (type) {
  887. case Constants.OBJ_COMMIT:
  888. case Constants.OBJ_TREE:
  889. case Constants.OBJ_BLOB:
  890. case Constants.OBJ_TAG:
  891. return type;
  892. case Constants.OBJ_OFS_DELTA: {
  893. int p = 1;
  894. while ((c & 0x80) != 0)
  895. c = ib[p++] & 0xff;
  896. c = ib[p++] & 0xff;
  897. long ofs = c & 127;
  898. while ((c & 128) != 0) {
  899. ofs += 1;
  900. c = ib[p++] & 0xff;
  901. ofs <<= 7;
  902. ofs += (c & 127);
  903. }
  904. pos = pos - ofs;
  905. continue;
  906. }
  907. case Constants.OBJ_REF_DELTA: {
  908. int p = 1;
  909. while ((c & 0x80) != 0)
  910. c = ib[p++] & 0xff;
  911. readFully(pos + p, ib, 0, 20, curs);
  912. pos = findDeltaBase(ObjectId.fromRaw(ib));
  913. continue;
  914. }
  915. default:
  916. throw new IOException(
  917. MessageFormat.format(JGitText.get().unknownObjectType,
  918. Integer.valueOf(type)));
  919. }
  920. }
  921. }
  922. long getObjectSize(final WindowCursor curs, final AnyObjectId id)
  923. throws IOException {
  924. final long offset = idx().findOffset(id);
  925. return 0 < offset ? getObjectSize(curs, offset) : -1;
  926. }
  927. long getObjectSize(final WindowCursor curs, final long pos)
  928. throws IOException {
  929. final byte[] ib = curs.tempId;
  930. readFully(pos, ib, 0, 20, curs);
  931. int c = ib[0] & 0xff;
  932. final int type = (c >> 4) & 7;
  933. long sz = c & 15;
  934. int shift = 4;
  935. int p = 1;
  936. while ((c & 0x80) != 0) {
  937. c = ib[p++] & 0xff;
  938. sz += ((long) (c & 0x7f)) << shift;
  939. shift += 7;
  940. }
  941. long deltaAt;
  942. switch (type) {
  943. case Constants.OBJ_COMMIT:
  944. case Constants.OBJ_TREE:
  945. case Constants.OBJ_BLOB:
  946. case Constants.OBJ_TAG:
  947. return sz;
  948. case Constants.OBJ_OFS_DELTA:
  949. c = ib[p++] & 0xff;
  950. while ((c & 128) != 0)
  951. c = ib[p++] & 0xff;
  952. deltaAt = pos + p;
  953. break;
  954. case Constants.OBJ_REF_DELTA:
  955. deltaAt = pos + p + 20;
  956. break;
  957. default:
  958. throw new IOException(MessageFormat.format(
  959. JGitText.get().unknownObjectType, Integer.valueOf(type)));
  960. }
  961. try {
  962. return BinaryDelta.getResultSize(getDeltaHeader(curs, deltaAt));
  963. } catch (DataFormatException e) {
  964. throw new CorruptObjectException(MessageFormat.format(
  965. JGitText.get().objectAtHasBadZlibStream, Long.valueOf(pos),
  966. getPackFile()));
  967. }
  968. }
  969. LocalObjectRepresentation representation(final WindowCursor curs,
  970. final AnyObjectId objectId) throws IOException {
  971. final long pos = idx().findOffset(objectId);
  972. if (pos < 0)
  973. return null;
  974. final byte[] ib = curs.tempId;
  975. readFully(pos, ib, 0, 20, curs);
  976. int c = ib[0] & 0xff;
  977. int p = 1;
  978. final int typeCode = (c >> 4) & 7;
  979. while ((c & 0x80) != 0)
  980. c = ib[p++] & 0xff;
  981. long len = (findEndOffset(pos) - pos);
  982. switch (typeCode) {
  983. case Constants.OBJ_COMMIT:
  984. case Constants.OBJ_TREE:
  985. case Constants.OBJ_BLOB:
  986. case Constants.OBJ_TAG:
  987. return LocalObjectRepresentation.newWhole(this, pos, len - p);
  988. case Constants.OBJ_OFS_DELTA: {
  989. c = ib[p++] & 0xff;
  990. long ofs = c & 127;
  991. while ((c & 128) != 0) {
  992. ofs += 1;
  993. c = ib[p++] & 0xff;
  994. ofs <<= 7;
  995. ofs += (c & 127);
  996. }
  997. ofs = pos - ofs;
  998. return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
  999. }
  1000. case Constants.OBJ_REF_DELTA: {
  1001. len -= p;
  1002. len -= Constants.OBJECT_ID_LENGTH;
  1003. readFully(pos + p, ib, 0, 20, curs);
  1004. ObjectId id = ObjectId.fromRaw(ib);
  1005. return LocalObjectRepresentation.newDelta(this, pos, len, id);
  1006. }
  1007. default:
  1008. throw new IOException(
  1009. MessageFormat.format(JGitText.get().unknownObjectType,
  1010. Integer.valueOf(typeCode)));
  1011. }
  1012. }
  1013. private long findEndOffset(final long startOffset)
  1014. throws IOException, CorruptObjectException {
  1015. final long maxOffset = length - 20;
  1016. return getReverseIdx().findNextOffset(startOffset, maxOffset);
  1017. }
  1018. synchronized PackBitmapIndex getBitmapIndex() throws IOException {
  1019. if (invalid || invalidBitmap)
  1020. return null;
  1021. if (bitmapIdx == null && hasExt(BITMAP_INDEX)) {
  1022. final PackBitmapIndex idx;
  1023. try {
  1024. idx = PackBitmapIndex.open(extFile(BITMAP_INDEX), idx(),
  1025. getReverseIdx());
  1026. } catch (FileNotFoundException e) {
  1027. // Once upon a time this bitmap file existed. Now it
  1028. // has been removed. Most likely an external gc has
  1029. // removed this packfile and the bitmap
  1030. invalidBitmap = true;
  1031. return null;
  1032. }
  1033. // At this point, idx() will have set packChecksum.
  1034. if (Arrays.equals(packChecksum, idx.packChecksum))
  1035. bitmapIdx = idx;
  1036. else
  1037. invalidBitmap = true;
  1038. }
  1039. return bitmapIdx;
  1040. }
  1041. private synchronized PackReverseIndex getReverseIdx() throws IOException {
  1042. if (reverseIdx == null)
  1043. reverseIdx = new PackReverseIndex(idx());
  1044. return reverseIdx;
  1045. }
  1046. private boolean isCorrupt(long offset) {
  1047. LongList list = corruptObjects;
  1048. if (list == null)
  1049. return false;
  1050. synchronized (list) {
  1051. return list.contains(offset);
  1052. }
  1053. }
  1054. private void setCorrupt(long offset) {
  1055. LongList list = corruptObjects;
  1056. if (list == null) {
  1057. synchronized (readLock) {
  1058. list = corruptObjects;
  1059. if (list == null) {
  1060. list = new LongList();
  1061. corruptObjects = list;
  1062. }
  1063. }
  1064. }
  1065. synchronized (list) {
  1066. list.add(offset);
  1067. }
  1068. }
  1069. private File extFile(PackExt ext) {
  1070. String p = packFile.getName();
  1071. int dot = p.lastIndexOf('.');
  1072. String b = (dot < 0) ? p : p.substring(0, dot);
  1073. return new File(packFile.getParentFile(), b + '.' + ext.getExtension());
  1074. }
  1075. private boolean hasExt(PackExt ext) {
  1076. return (extensions & ext.getBit()) != 0;
  1077. }
  1078. }