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ů.

PackFile.java 33KB

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