Pack.java

  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> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.internal.storage.file;

  13. import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
  14. import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;

  15. import java.io.EOFException;
  16. import java.io.File;
  17. import java.io.FileNotFoundException;
  18. import java.io.IOException;
  19. import java.io.InterruptedIOException;
  20. import java.io.RandomAccessFile;
  21. import java.nio.MappedByteBuffer;
  22. import java.nio.channels.FileChannel.MapMode;
  23. import java.nio.file.AccessDeniedException;
  24. import java.nio.file.NoSuchFileException;
  25. import java.text.MessageFormat;
  26. import java.time.Instant;
  27. import java.util.Arrays;
  28. import java.util.Collections;
  29. import java.util.Comparator;
  30. import java.util.Iterator;
  31. import java.util.Set;
  32. import java.util.concurrent.atomic.AtomicInteger;
  33. import java.util.zip.CRC32;
  34. import java.util.zip.DataFormatException;
  35. import java.util.zip.Inflater;

  36. import org.eclipse.jgit.annotations.Nullable;
  37. import org.eclipse.jgit.errors.CorruptObjectException;
  38. import org.eclipse.jgit.errors.LargeObjectException;
  39. import org.eclipse.jgit.errors.MissingObjectException;
  40. import org.eclipse.jgit.errors.NoPackSignatureException;
  41. import org.eclipse.jgit.errors.PackInvalidException;
  42. import org.eclipse.jgit.errors.PackMismatchException;
  43. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  44. import org.eclipse.jgit.errors.UnpackException;
  45. import org.eclipse.jgit.errors.UnsupportedPackIndexVersionException;
  46. import org.eclipse.jgit.errors.UnsupportedPackVersionException;
  47. import org.eclipse.jgit.internal.JGitText;
  48. import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
  49. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  50. import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
  51. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  52. import org.eclipse.jgit.lib.AnyObjectId;
  53. import org.eclipse.jgit.lib.Constants;
  54. import org.eclipse.jgit.lib.ObjectId;
  55. import org.eclipse.jgit.lib.ObjectLoader;
  56. import org.eclipse.jgit.util.LongList;
  57. import org.eclipse.jgit.util.NB;
  58. import org.eclipse.jgit.util.RawParseUtils;
  59. import org.slf4j.Logger;
  60. import org.slf4j.LoggerFactory;

  61. /**
  62.  * A Git version 2 pack file representation. A pack file contains Git objects in
  63.  * delta packed format yielding high compression of lots of object where some
  64.  * objects are similar.
  65.  */
  66. public class Pack implements Iterable<PackIndex.MutableEntry> {
  67.     private static final Logger LOG = LoggerFactory.getLogger(Pack.class);

  68.     /**
  69.      * Sorts PackFiles to be most recently created to least recently created.
  70.      */
  71.     public static final Comparator<Pack> SORT = (a, b) -> b.packLastModified
  72.             .compareTo(a.packLastModified);

  73.     private final PackFile packFile;

  74.     private PackFile keepFile;

  75.     final int hash;

  76.     private RandomAccessFile fd;

  77.     /** Serializes reads performed against {@link #fd}. */
  78.     private final Object readLock = new Object();

  79.     long length;

  80.     private int activeWindows;

  81.     private int activeCopyRawData;

  82.     Instant packLastModified;

  83.     private PackFileSnapshot fileSnapshot;

  84.     private volatile boolean invalid;

  85.     private volatile Exception invalidatingCause;

  86.     @Nullable
  87.     private PackFile bitmapIdxFile;

  88.     private AtomicInteger transientErrorCount = new AtomicInteger();

  89.     private byte[] packChecksum;

  90.     private volatile PackIndex loadedIdx;

  91.     private PackReverseIndex reverseIdx;

  92.     private PackBitmapIndex bitmapIdx;

  93.     /**
  94.      * Objects we have tried to read, and discovered to be corrupt.
  95.      * <p>
  96.      * The list is allocated after the first corruption is found, and filled in
  97.      * as more entries are discovered. Typically this list is never used, as
  98.      * pack files do not usually contain corrupt objects.
  99.      */
  100.     private volatile LongList corruptObjects;

  101.     /**
  102.      * Construct a reader for an existing, pre-indexed packfile.
  103.      *
  104.      * @param packFile
  105.      *            path of the <code>.pack</code> file holding the data.
  106.      * @param bitmapIdxFile
  107.      *            existing bitmap index file with the same base as the pack
  108.      */
  109.     public Pack(File packFile, @Nullable PackFile bitmapIdxFile) {
  110.         this.packFile = new PackFile(packFile);
  111.         this.fileSnapshot = PackFileSnapshot.save(packFile);
  112.         this.packLastModified = fileSnapshot.lastModifiedInstant();
  113.         this.bitmapIdxFile = bitmapIdxFile;

  114.         // Multiply by 31 here so we can more directly combine with another
  115.         // value in WindowCache.hash(), without doing the multiply there.
  116.         //
  117.         hash = System.identityHashCode(this) * 31;
  118.         length = Long.MAX_VALUE;
  119.     }

  120.     private PackIndex idx() throws IOException {
  121.         PackIndex idx = loadedIdx;
  122.         if (idx == null) {
  123.             synchronized (this) {
  124.                 idx = loadedIdx;
  125.                 if (idx == null) {
  126.                     if (invalid) {
  127.                         throw new PackInvalidException(packFile,
  128.                                 invalidatingCause);
  129.                     }
  130.                     try {
  131.                         long start = System.currentTimeMillis();
  132.                         PackFile idxFile = packFile.create(INDEX);
  133.                         idx = PackIndex.open(idxFile);
  134.                         if (LOG.isDebugEnabled()) {
  135.                             LOG.debug(String.format(
  136.                                     "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
  137.                                     idxFile.getAbsolutePath(),
  138.                                     Float.valueOf(idxFile.length()
  139.                                             / (1024f * 1024)),
  140.                                     Long.valueOf(System.currentTimeMillis()
  141.                                             - start)));
  142.                         }

  143.                         if (packChecksum == null) {
  144.                             packChecksum = idx.packChecksum;
  145.                             fileSnapshot.setChecksum(
  146.                                     ObjectId.fromRaw(packChecksum));
  147.                         } else if (!Arrays.equals(packChecksum,
  148.                                 idx.packChecksum)) {
  149.                             throw new PackMismatchException(MessageFormat
  150.                                     .format(JGitText.get().packChecksumMismatch,
  151.                                             packFile.getPath(),
  152.                                             ObjectId.fromRaw(packChecksum)
  153.                                                     .name(),
  154.                                             ObjectId.fromRaw(idx.packChecksum)
  155.                                                     .name()));
  156.                         }
  157.                         loadedIdx = idx;
  158.                     } catch (InterruptedIOException e) {
  159.                         // don't invalidate the pack, we are interrupted from
  160.                         // another thread
  161.                         throw e;
  162.                     } catch (IOException e) {
  163.                         invalid = true;
  164.                         invalidatingCause = e;
  165.                         throw e;
  166.                     }
  167.                 }
  168.             }
  169.         }
  170.         return idx;
  171.     }
  172.     /**
  173.      * Get the File object which locates this pack on disk.
  174.      *
  175.      * @return the File object which locates this pack on disk.
  176.      */
  177.     public PackFile getPackFile() {
  178.         return packFile;
  179.     }

  180.     /**
  181.      * Get the index for this pack file.
  182.      *
  183.      * @return the index for this pack file.
  184.      * @throws java.io.IOException
  185.      */
  186.     public PackIndex getIndex() throws IOException {
  187.         return idx();
  188.     }

  189.     /**
  190.      * Get name extracted from {@code pack-*.pack} pattern.
  191.      *
  192.      * @return name extracted from {@code pack-*.pack} pattern.
  193.      */
  194.     public String getPackName() {
  195.         return packFile.getId();
  196.     }

  197.     /**
  198.      * Determine if an object is contained within the pack file.
  199.      * <p>
  200.      * For performance reasons only the index file is searched; the main pack
  201.      * content is ignored entirely.
  202.      * </p>
  203.      *
  204.      * @param id
  205.      *            the object to look for. Must not be null.
  206.      * @return true if the object is in this pack; false otherwise.
  207.      * @throws java.io.IOException
  208.      *             the index file cannot be loaded into memory.
  209.      */
  210.     public boolean hasObject(AnyObjectId id) throws IOException {
  211.         final long offset = idx().findOffset(id);
  212.         return 0 < offset && !isCorrupt(offset);
  213.     }

  214.     /**
  215.      * Determines whether a .keep file exists for this pack file.
  216.      *
  217.      * @return true if a .keep file exist.
  218.      */
  219.     public boolean shouldBeKept() {
  220.         if (keepFile == null) {
  221.             keepFile = packFile.create(KEEP);
  222.         }
  223.         return keepFile.exists();
  224.     }

  225.     /**
  226.      * Get an object from this pack.
  227.      *
  228.      * @param curs
  229.      *            temporary working space associated with the calling thread.
  230.      * @param id
  231.      *            the object to obtain from the pack. Must not be null.
  232.      * @return the object loader for the requested object if it is contained in
  233.      *         this pack; null if the object was not found.
  234.      * @throws IOException
  235.      *             the pack file or the index could not be read.
  236.      */
  237.     ObjectLoader get(WindowCursor curs, AnyObjectId id)
  238.             throws IOException {
  239.         final long offset = idx().findOffset(id);
  240.         return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
  241.     }

  242.     void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
  243.             throws IOException {
  244.         idx().resolve(matches, id, matchLimit);
  245.     }

  246.     /**
  247.      * Close the resources utilized by this repository
  248.      */
  249.     public void close() {
  250.         WindowCache.purge(this);
  251.         synchronized (this) {
  252.             loadedIdx = null;
  253.             reverseIdx = null;
  254.         }
  255.     }

  256.     /**
  257.      * {@inheritDoc}
  258.      * <p>
  259.      * Provide iterator over entries in associated pack index, that should also
  260.      * exist in this pack file. Objects returned by such iterator are mutable
  261.      * during iteration.
  262.      * <p>
  263.      * Iterator returns objects in SHA-1 lexicographical order.
  264.      * </p>
  265.      *
  266.      * @see PackIndex#iterator()
  267.      */
  268.     @Override
  269.     public Iterator<PackIndex.MutableEntry> iterator() {
  270.         try {
  271.             return idx().iterator();
  272.         } catch (IOException e) {
  273.             return Collections.<PackIndex.MutableEntry> emptyList().iterator();
  274.         }
  275.     }

  276.     /**
  277.      * Obtain the total number of objects available in this pack. This method
  278.      * relies on pack index, giving number of effectively available objects.
  279.      *
  280.      * @return number of objects in index of this pack, likewise in this pack
  281.      * @throws IOException
  282.      *             the index file cannot be loaded into memory.
  283.      */
  284.     long getObjectCount() throws IOException {
  285.         return idx().getObjectCount();
  286.     }

  287.     /**
  288.      * Search for object id with the specified start offset in associated pack
  289.      * (reverse) index.
  290.      *
  291.      * @param offset
  292.      *            start offset of object to find
  293.      * @return object id for this offset, or null if no object was found
  294.      * @throws IOException
  295.      *             the index file cannot be loaded into memory.
  296.      */
  297.     ObjectId findObjectForOffset(long offset) throws IOException {
  298.         return getReverseIdx().findObject(offset);
  299.     }

  300.     /**
  301.      * Return the @{@link FileSnapshot} associated to the underlying packfile
  302.      * that has been used when the object was created.
  303.      *
  304.      * @return the packfile @{@link FileSnapshot} that the object is loaded from.
  305.      */
  306.     PackFileSnapshot getFileSnapshot() {
  307.         return fileSnapshot;
  308.     }

  309.     AnyObjectId getPackChecksum() {
  310.         return ObjectId.fromRaw(packChecksum);
  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.         if (invalid) {
  576.             openFail(true, invalidatingCause);
  577.             throw new PackInvalidException(packFile, invalidatingCause);
  578.         }
  579.         try {
  580.             synchronized (readLock) {
  581.                 fd = new RandomAccessFile(packFile, "r"); //$NON-NLS-1$
  582.                 length = fd.length();
  583.                 onOpenPack();
  584.             }
  585.         } catch (InterruptedIOException e) {
  586.             // don't invalidate the pack, we are interrupted from another thread
  587.             openFail(false, e);
  588.             throw e;
  589.         } catch (FileNotFoundException fn) {
  590.             // don't invalidate the pack if opening an existing file failed
  591.             // since it may be related to a temporary lack of resources (e.g.
  592.             // max open files)
  593.             openFail(!packFile.exists(), fn);
  594.             throw fn;
  595.         } catch (EOFException | AccessDeniedException | NoSuchFileException
  596.                 | CorruptObjectException | NoPackSignatureException
  597.                 | PackMismatchException | UnpackException
  598.                 | UnsupportedPackIndexVersionException
  599.                 | UnsupportedPackVersionException pe) {
  600.             // exceptions signaling permanent problems with a pack
  601.             openFail(true, pe);
  602.             throw pe;
  603.         } catch (IOException | RuntimeException ge) {
  604.             // generic exceptions could be transient so we should not mark the
  605.             // pack invalid to avoid false MissingObjectExceptions
  606.             openFail(false, ge);
  607.             throw ge;
  608.         }
  609.     }

  610.     private void openFail(boolean invalidate, Exception cause) {
  611.         activeWindows = 0;
  612.         activeCopyRawData = 0;
  613.         invalid = invalidate;
  614.         invalidatingCause = cause;
  615.         doClose();
  616.     }

  617.     private void doClose() {
  618.         synchronized (readLock) {
  619.             if (fd != null) {
  620.                 try {
  621.                     fd.close();
  622.                 } catch (IOException err) {
  623.                     // Ignore a close event. We had it open only for reading.
  624.                     // There should not be errors related to network buffers
  625.                     // not flushed, etc.
  626.                 }
  627.                 fd = null;
  628.             }
  629.         }
  630.     }

  631.     ByteArrayWindow read(long pos, int size) throws IOException {
  632.         synchronized (readLock) {
  633.             if (invalid || fd == null) {
  634.                 // Due to concurrency between a read and another packfile invalidation thread
  635.                 // one thread could come up to this point and then fail with NPE.
  636.                 // Detect the situation and throw a proper exception so that can be properly
  637.                 // managed by the main packfile search loop and the Git client won't receive
  638.                 // any failures.
  639.                 throw new PackInvalidException(packFile, invalidatingCause);
  640.             }
  641.             if (length < pos + size)
  642.                 size = (int) (length - pos);
  643.             final byte[] buf = new byte[size];
  644.             fd.seek(pos);
  645.             fd.readFully(buf, 0, size);
  646.             return new ByteArrayWindow(this, pos, buf);
  647.         }
  648.     }

  649.     ByteWindow mmap(long pos, int size) throws IOException {
  650.         synchronized (readLock) {
  651.             if (length < pos + size)
  652.                 size = (int) (length - pos);

  653.             MappedByteBuffer map;
  654.             try {
  655.                 map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  656.             } catch (IOException ioe1) {
  657.                 // The most likely reason this failed is the JVM has run out
  658.                 // of virtual memory. We need to discard quickly, and try to
  659.                 // force the GC to finalize and release any existing mappings.
  660.                 //
  661.                 System.gc();
  662.                 System.runFinalization();
  663.                 map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
  664.             }

  665.             if (map.hasArray())
  666.                 return new ByteArrayWindow(this, pos, map.array());
  667.             return new ByteBufferWindow(this, pos, map);
  668.         }
  669.     }

  670.     private void onOpenPack() throws IOException {
  671.         final PackIndex idx = idx();
  672.         final byte[] buf = new byte[20];

  673.         fd.seek(0);
  674.         fd.readFully(buf, 0, 12);
  675.         if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4) {
  676.             throw new NoPackSignatureException(JGitText.get().notAPACKFile);
  677.         }
  678.         final long vers = NB.decodeUInt32(buf, 4);
  679.         final long packCnt = NB.decodeUInt32(buf, 8);
  680.         if (vers != 2 && vers != 3) {
  681.             throw new UnsupportedPackVersionException(vers);
  682.         }

  683.         if (packCnt != idx.getObjectCount()) {
  684.             throw new PackMismatchException(MessageFormat.format(
  685.                     JGitText.get().packObjectCountMismatch,
  686.                     Long.valueOf(packCnt), Long.valueOf(idx.getObjectCount()),
  687.                     getPackFile()));
  688.         }

  689.         fd.seek(length - 20);
  690.         fd.readFully(buf, 0, 20);
  691.         if (!Arrays.equals(buf, packChecksum)) {
  692.             throw new PackMismatchException(MessageFormat.format(
  693.                     JGitText.get().packChecksumMismatch,
  694.                     getPackFile(),
  695.                     ObjectId.fromRaw(buf).name(),
  696.                     ObjectId.fromRaw(idx.packChecksum).name()));
  697.         }
  698.     }

  699.     ObjectLoader load(WindowCursor curs, long pos)
  700.             throws IOException, LargeObjectException {
  701.         try {
  702.             final byte[] ib = curs.tempId;
  703.             Delta delta = null;
  704.             byte[] data = null;
  705.             int type = Constants.OBJ_BAD;
  706.             boolean cached = false;

  707.             SEARCH: for (;;) {
  708.                 readFully(pos, ib, 0, 20, curs);
  709.                 int c = ib[0] & 0xff;
  710.                 final int typeCode = (c >> 4) & 7;
  711.                 long sz = c & 15;
  712.                 int shift = 4;
  713.                 int p = 1;
  714.                 while ((c & 0x80) != 0) {
  715.                     c = ib[p++] & 0xff;
  716.                     sz += ((long) (c & 0x7f)) << shift;
  717.                     shift += 7;
  718.                 }

  719.                 switch (typeCode) {
  720.                 case Constants.OBJ_COMMIT:
  721.                 case Constants.OBJ_TREE:
  722.                 case Constants.OBJ_BLOB:
  723.                 case Constants.OBJ_TAG: {
  724.                     if (delta != null || sz < curs.getStreamFileThreshold()) {
  725.                         data = decompress(pos + p, (int) sz, curs);
  726.                     }

  727.                     if (delta != null) {
  728.                         type = typeCode;
  729.                         break SEARCH;
  730.                     }

  731.                     if (data != null) {
  732.                         return new ObjectLoader.SmallObject(typeCode, data);
  733.                     }
  734.                     return new LargePackedWholeObject(typeCode, sz, pos, p,
  735.                             this, curs.db);
  736.                 }

  737.                 case Constants.OBJ_OFS_DELTA: {
  738.                     c = ib[p++] & 0xff;
  739.                     long base = c & 127;
  740.                     while ((c & 128) != 0) {
  741.                         base += 1;
  742.                         c = ib[p++] & 0xff;
  743.                         base <<= 7;
  744.                         base += (c & 127);
  745.                     }
  746.                     base = pos - base;
  747.                     delta = new Delta(delta, pos, (int) sz, p, base);
  748.                     if (sz != delta.deltaSize)
  749.                         break SEARCH;

  750.                     DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
  751.                     if (e != null) {
  752.                         type = e.type;
  753.                         data = e.data;
  754.                         cached = true;
  755.                         break SEARCH;
  756.                     }
  757.                     pos = base;
  758.                     continue SEARCH;
  759.                 }

  760.                 case Constants.OBJ_REF_DELTA: {
  761.                     readFully(pos + p, ib, 0, 20, curs);
  762.                     long base = findDeltaBase(ObjectId.fromRaw(ib));
  763.                     delta = new Delta(delta, pos, (int) sz, p + 20, base);
  764.                     if (sz != delta.deltaSize)
  765.                         break SEARCH;

  766.                     DeltaBaseCache.Entry e = curs.getDeltaBaseCache().get(this, base);
  767.                     if (e != null) {
  768.                         type = e.type;
  769.                         data = e.data;
  770.                         cached = true;
  771.                         break SEARCH;
  772.                     }
  773.                     pos = base;
  774.                     continue SEARCH;
  775.                 }

  776.                 default:
  777.                     throw new IOException(MessageFormat.format(
  778.                             JGitText.get().unknownObjectType,
  779.                             Integer.valueOf(typeCode)));
  780.                 }
  781.             }

  782.             // At this point there is at least one delta to apply to data.
  783.             // (Whole objects with no deltas to apply return early above.)

  784.             if (data == null)
  785.                 throw new IOException(JGitText.get().inMemoryBufferLimitExceeded);

  786.             assert(delta != null);
  787.             do {
  788.                 // Cache only the base immediately before desired object.
  789.                 if (cached)
  790.                     cached = false;
  791.                 else if (delta.next == null)
  792.                     curs.getDeltaBaseCache().store(this, delta.basePos, data, type);

  793.                 pos = delta.deltaPos;

  794.                 final byte[] cmds = decompress(pos + delta.hdrLen,
  795.                         delta.deltaSize, curs);
  796.                 if (cmds == null) {
  797.                     data = null; // Discard base in case of OutOfMemoryError
  798.                     throw new LargeObjectException.OutOfMemory(new OutOfMemoryError());
  799.                 }

  800.                 final long sz = BinaryDelta.getResultSize(cmds);
  801.                 if (Integer.MAX_VALUE <= sz)
  802.                     throw new LargeObjectException.ExceedsByteArrayLimit();

  803.                 final byte[] result;
  804.                 try {
  805.                     result = new byte[(int) sz];
  806.                 } catch (OutOfMemoryError tooBig) {
  807.                     data = null; // Discard base in case of OutOfMemoryError
  808.                     throw new LargeObjectException.OutOfMemory(tooBig);
  809.                 }

  810.                 BinaryDelta.apply(data, cmds, result);
  811.                 data = result;
  812.                 delta = delta.next;
  813.             } while (delta != null);

  814.             return new ObjectLoader.SmallObject(type, data);

  815.         } catch (DataFormatException dfe) {
  816.             throw new CorruptObjectException(
  817.                     MessageFormat.format(
  818.                             JGitText.get().objectAtHasBadZlibStream,
  819.                             Long.valueOf(pos), getPackFile()),
  820.                     dfe);
  821.         }
  822.     }

  823.     private long findDeltaBase(ObjectId baseId) throws IOException,
  824.             MissingObjectException {
  825.         long ofs = idx().findOffset(baseId);
  826.         if (ofs < 0)
  827.             throw new MissingObjectException(baseId,
  828.                     JGitText.get().missingDeltaBase);
  829.         return ofs;
  830.     }

  831.     private static class Delta {
  832.         /** Child that applies onto this object. */
  833.         final Delta next;

  834.         /** Offset of the delta object. */
  835.         final long deltaPos;

  836.         /** Size of the inflated delta stream. */
  837.         final int deltaSize;

  838.         /** Total size of the delta's pack entry header (including base). */
  839.         final int hdrLen;

  840.         /** Offset of the base object this delta applies onto. */
  841.         final long basePos;

  842.         Delta(Delta next, long ofs, int sz, int hdrLen, long baseOffset) {
  843.             this.next = next;
  844.             this.deltaPos = ofs;
  845.             this.deltaSize = sz;
  846.             this.hdrLen = hdrLen;
  847.             this.basePos = baseOffset;
  848.         }
  849.     }

  850.     byte[] getDeltaHeader(WindowCursor wc, long pos)
  851.             throws IOException, DataFormatException {
  852.         // The delta stream starts as two variable length integers. If we
  853.         // assume they are 64 bits each, we need 16 bytes to encode them,
  854.         // plus 2 extra bytes for the variable length overhead. So 18 is
  855.         // the longest delta instruction header.
  856.         //
  857.         final byte[] hdr = new byte[18];
  858.         wc.inflate(this, pos, hdr, true /* headerOnly */);
  859.         return hdr;
  860.     }

  861.     int getObjectType(WindowCursor curs, long pos) throws IOException {
  862.         final byte[] ib = curs.tempId;
  863.         for (;;) {
  864.             readFully(pos, ib, 0, 20, curs);
  865.             int c = ib[0] & 0xff;
  866.             final int type = (c >> 4) & 7;

  867.             switch (type) {
  868.             case Constants.OBJ_COMMIT:
  869.             case Constants.OBJ_TREE:
  870.             case Constants.OBJ_BLOB:
  871.             case Constants.OBJ_TAG:
  872.                 return type;

  873.             case Constants.OBJ_OFS_DELTA: {
  874.                 int p = 1;
  875.                 while ((c & 0x80) != 0)
  876.                     c = ib[p++] & 0xff;
  877.                 c = ib[p++] & 0xff;
  878.                 long ofs = c & 127;
  879.                 while ((c & 128) != 0) {
  880.                     ofs += 1;
  881.                     c = ib[p++] & 0xff;
  882.                     ofs <<= 7;
  883.                     ofs += (c & 127);
  884.                 }
  885.                 pos = pos - ofs;
  886.                 continue;
  887.             }

  888.             case Constants.OBJ_REF_DELTA: {
  889.                 int p = 1;
  890.                 while ((c & 0x80) != 0)
  891.                     c = ib[p++] & 0xff;
  892.                 readFully(pos + p, ib, 0, 20, curs);
  893.                 pos = findDeltaBase(ObjectId.fromRaw(ib));
  894.                 continue;
  895.             }

  896.             default:
  897.                 throw new IOException(
  898.                         MessageFormat.format(JGitText.get().unknownObjectType,
  899.                                 Integer.valueOf(type)));
  900.             }
  901.         }
  902.     }

  903.     long getObjectSize(WindowCursor curs, AnyObjectId id)
  904.             throws IOException {
  905.         final long offset = idx().findOffset(id);
  906.         return 0 < offset ? getObjectSize(curs, offset) : -1;
  907.     }

  908.     long getObjectSize(WindowCursor curs, long pos)
  909.             throws IOException {
  910.         final byte[] ib = curs.tempId;
  911.         readFully(pos, ib, 0, 20, curs);
  912.         int c = ib[0] & 0xff;
  913.         final int type = (c >> 4) & 7;
  914.         long sz = c & 15;
  915.         int shift = 4;
  916.         int p = 1;
  917.         while ((c & 0x80) != 0) {
  918.             c = ib[p++] & 0xff;
  919.             sz += ((long) (c & 0x7f)) << shift;
  920.             shift += 7;
  921.         }

  922.         long deltaAt;
  923.         switch (type) {
  924.         case Constants.OBJ_COMMIT:
  925.         case Constants.OBJ_TREE:
  926.         case Constants.OBJ_BLOB:
  927.         case Constants.OBJ_TAG:
  928.             return sz;

  929.         case Constants.OBJ_OFS_DELTA:
  930.             c = ib[p++] & 0xff;
  931.             while ((c & 128) != 0)
  932.                 c = ib[p++] & 0xff;
  933.             deltaAt = pos + p;
  934.             break;

  935.         case Constants.OBJ_REF_DELTA:
  936.             deltaAt = pos + p + 20;
  937.             break;

  938.         default:
  939.             throw new IOException(MessageFormat.format(
  940.                     JGitText.get().unknownObjectType, Integer.valueOf(type)));
  941.         }

  942.         try {
  943.             return BinaryDelta.getResultSize(getDeltaHeader(curs, deltaAt));
  944.         } catch (DataFormatException e) {
  945.             throw new CorruptObjectException(MessageFormat.format(
  946.                     JGitText.get().objectAtHasBadZlibStream, Long.valueOf(pos),
  947.                     getPackFile()), e);
  948.         }
  949.     }

  950.     LocalObjectRepresentation representation(final WindowCursor curs,
  951.             final AnyObjectId objectId) throws IOException {
  952.         final long pos = idx().findOffset(objectId);
  953.         if (pos < 0)
  954.             return null;

  955.         final byte[] ib = curs.tempId;
  956.         readFully(pos, ib, 0, 20, curs);
  957.         int c = ib[0] & 0xff;
  958.         int p = 1;
  959.         final int typeCode = (c >> 4) & 7;
  960.         while ((c & 0x80) != 0)
  961.             c = ib[p++] & 0xff;

  962.         long len = (findEndOffset(pos) - pos);
  963.         switch (typeCode) {
  964.         case Constants.OBJ_COMMIT:
  965.         case Constants.OBJ_TREE:
  966.         case Constants.OBJ_BLOB:
  967.         case Constants.OBJ_TAG:
  968.             return LocalObjectRepresentation.newWhole(this, pos, len - p);

  969.         case Constants.OBJ_OFS_DELTA: {
  970.             c = ib[p++] & 0xff;
  971.             long ofs = c & 127;
  972.             while ((c & 128) != 0) {
  973.                 ofs += 1;
  974.                 c = ib[p++] & 0xff;
  975.                 ofs <<= 7;
  976.                 ofs += (c & 127);
  977.             }
  978.             ofs = pos - ofs;
  979.             return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
  980.         }

  981.         case Constants.OBJ_REF_DELTA: {
  982.             len -= p;
  983.             len -= Constants.OBJECT_ID_LENGTH;
  984.             readFully(pos + p, ib, 0, 20, curs);
  985.             ObjectId id = ObjectId.fromRaw(ib);
  986.             return LocalObjectRepresentation.newDelta(this, pos, len, id);
  987.         }

  988.         default:
  989.             throw new IOException(
  990.                     MessageFormat.format(JGitText.get().unknownObjectType,
  991.                             Integer.valueOf(typeCode)));
  992.         }
  993.     }

  994.     private long findEndOffset(long startOffset)
  995.             throws IOException, CorruptObjectException {
  996.         final long maxOffset = length - 20;
  997.         return getReverseIdx().findNextOffset(startOffset, maxOffset);
  998.     }

  999.     synchronized PackBitmapIndex getBitmapIndex() throws IOException {
  1000.         if (invalid || bitmapIdxFile == null) {
  1001.             return null;
  1002.         }
  1003.         if (bitmapIdx == null) {
  1004.             final PackBitmapIndex idx;
  1005.             try {
  1006.                 idx = PackBitmapIndex.open(bitmapIdxFile, idx(),
  1007.                         getReverseIdx());
  1008.             } catch (FileNotFoundException e) {
  1009.                 // Once upon a time this bitmap file existed. Now it
  1010.                 // has been removed. Most likely an external gc  has
  1011.                 // removed this packfile and the bitmap
  1012.                 bitmapIdxFile = null;
  1013.                 return null;
  1014.             }

  1015.             // At this point, idx() will have set packChecksum.
  1016.             if (Arrays.equals(packChecksum, idx.packChecksum)) {
  1017.                 bitmapIdx = idx;
  1018.             } else {
  1019.                 bitmapIdxFile = null;
  1020.             }
  1021.         }
  1022.         return bitmapIdx;
  1023.     }

  1024.     private synchronized PackReverseIndex getReverseIdx() throws IOException {
  1025.         if (reverseIdx == null)
  1026.             reverseIdx = new PackReverseIndex(idx());
  1027.         return reverseIdx;
  1028.     }

  1029.     private boolean isCorrupt(long offset) {
  1030.         LongList list = corruptObjects;
  1031.         if (list == null)
  1032.             return false;
  1033.         synchronized (list) {
  1034.             return list.contains(offset);
  1035.         }
  1036.     }

  1037.     private void setCorrupt(long offset) {
  1038.         LongList list = corruptObjects;
  1039.         if (list == null) {
  1040.             synchronized (readLock) {
  1041.                 list = corruptObjects;
  1042.                 if (list == null) {
  1043.                     list = new LongList();
  1044.                     corruptObjects = list;
  1045.                 }
  1046.             }
  1047.         }
  1048.         synchronized (list) {
  1049.             list.add(offset);
  1050.         }
  1051.     }

  1052.     @SuppressWarnings("nls")
  1053.     @Override
  1054.     public String toString() {
  1055.         return "Pack [packFileName=" + packFile.getName() + ", length="
  1056.                 + packFile.length() + ", packChecksum="
  1057.                 + ObjectId.fromRaw(packChecksum).name() + "]";
  1058.     }
  1059. }