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.PackOutputStream;
  50. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  51. import org.eclipse.jgit.lib.AnyObjectId;
  52. import org.eclipse.jgit.lib.Constants;
  53. import org.eclipse.jgit.lib.ObjectId;
  54. import org.eclipse.jgit.lib.ObjectLoader;
  55. import org.eclipse.jgit.util.LongList;
  56. import org.eclipse.jgit.util.NB;
  57. import org.eclipse.jgit.util.RawParseUtils;
  58. import org.slf4j.Logger;
  59. import org.slf4j.LoggerFactory;

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

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

  72.     private final PackFile packFile;

  73.     private PackFile keepFile;

  74.     final int hash;

  75.     private RandomAccessFile fd;

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

  78.     long length;

  79.     private int activeWindows;

  80.     private int activeCopyRawData;

  81.     Instant packLastModified;

  82.     private PackFileSnapshot fileSnapshot;

  83.     private volatile boolean invalid;

  84.     private volatile Exception invalidatingCause;

  85.     @Nullable
  86.     private PackFile bitmapIdxFile;

  87.     private AtomicInteger transientErrorCount = new AtomicInteger();

  88.     private byte[] packChecksum;

  89.     private volatile PackIndex loadedIdx;

  90.     private PackReverseIndex reverseIdx;

  91.     private PackBitmapIndex bitmapIdx;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  308.     AnyObjectId getPackChecksum() {
  309.         return ObjectId.fromRaw(packChecksum);
  310.     }

  311.     private final byte[] decompress(final long position, final int sz,
  312.             final WindowCursor curs) throws IOException, DataFormatException {
  313.         byte[] dstbuf;
  314.         try {
  315.             dstbuf = new byte[sz];
  316.         } catch (OutOfMemoryError noMemory) {
  317.             // The size may be larger than our heap allows, return null to
  318.             // let the caller know allocation isn't possible and it should
  319.             // use the large object streaming approach instead.
  320.             //
  321.             // For example, this can occur when sz is 640 MB, and JRE
  322.             // maximum heap size is only 256 MB. Even if the JRE has
  323.             // 200 MB free, it cannot allocate a 640 MB byte array.
  324.             return null;
  325.         }

  326.         if (curs.inflate(this, position, dstbuf, false) != sz)
  327.             throw new EOFException(MessageFormat.format(
  328.                     JGitText.get().shortCompressedStreamAt,
  329.                     Long.valueOf(position)));
  330.         return dstbuf;
  331.     }

  332.     void copyPackAsIs(PackOutputStream out, WindowCursor curs)
  333.             throws IOException {
  334.         // Pin the first window, this ensures the length is accurate.
  335.         curs.pin(this, 0);
  336.         curs.copyPackAsIs(this, length, out);
  337.     }

  338.     final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
  339.             boolean validate, WindowCursor curs) throws IOException,
  340.             StoredObjectRepresentationNotAvailableException {
  341.         beginCopyAsIs();
  342.         try {
  343.             copyAsIs2(out, src, validate, curs);
  344.         } finally {
  345.             endCopyAsIs();
  346.         }
  347.     }

  348.     private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
  349.             boolean validate, WindowCursor curs) throws IOException,
  350.             StoredObjectRepresentationNotAvailableException {
  351.         final CRC32 crc1 = validate ? new CRC32() : null;
  352.         final CRC32 crc2 = validate ? new CRC32() : null;
  353.         final byte[] buf = out.getCopyBuffer();

  354.         // Rip apart the header so we can discover the size.
  355.         //
  356.         readFully(src.offset, buf, 0, 20, curs);
  357.         int c = buf[0] & 0xff;
  358.         final int typeCode = (c >> 4) & 7;
  359.         long inflatedLength = c & 15;
  360.         int shift = 4;
  361.         int headerCnt = 1;
  362.         while ((c & 0x80) != 0) {
  363.             c = buf[headerCnt++] & 0xff;
  364.             inflatedLength += ((long) (c & 0x7f)) << shift;
  365.             shift += 7;
  366.         }

  367.         if (typeCode == Constants.OBJ_OFS_DELTA) {
  368.             do {
  369.                 c = buf[headerCnt++] & 0xff;
  370.             } while ((c & 128) != 0);
  371.             if (validate) {
  372.                 assert(crc1 != null && crc2 != null);
  373.                 crc1.update(buf, 0, headerCnt);
  374.                 crc2.update(buf, 0, headerCnt);
  375.             }
  376.         } else if (typeCode == Constants.OBJ_REF_DELTA) {
  377.             if (validate) {
  378.                 assert(crc1 != null && crc2 != null);
  379.                 crc1.update(buf, 0, headerCnt);
  380.                 crc2.update(buf, 0, headerCnt);
  381.             }

  382.             readFully(src.offset + headerCnt, buf, 0, 20, curs);
  383.             if (validate) {
  384.                 assert(crc1 != null && crc2 != null);
  385.                 crc1.update(buf, 0, 20);
  386.                 crc2.update(buf, 0, 20);
  387.             }
  388.             headerCnt += 20;
  389.         } else if (validate) {
  390.             assert(crc1 != null && crc2 != null);
  391.             crc1.update(buf, 0, headerCnt);
  392.             crc2.update(buf, 0, headerCnt);
  393.         }

  394.         final long dataOffset = src.offset + headerCnt;
  395.         final long dataLength = src.length;
  396.         final long expectedCRC;
  397.         final ByteArrayWindow quickCopy;

  398.         // Verify the object isn't corrupt before sending. If it is,
  399.         // we report it missing instead.
  400.         //
  401.         try {
  402.             quickCopy = curs.quickCopy(this, dataOffset, dataLength);

  403.             if (validate && idx().hasCRC32Support()) {
  404.                 assert(crc1 != null);
  405.                 // Index has the CRC32 code cached, validate the object.
  406.                 //
  407.                 expectedCRC = idx().findCRC32(src);
  408.                 if (quickCopy != null) {
  409.                     quickCopy.crc32(crc1, dataOffset, (int) dataLength);
  410.                 } else {
  411.                     long pos = dataOffset;
  412.                     long cnt = dataLength;
  413.                     while (cnt > 0) {
  414.                         final int n = (int) Math.min(cnt, buf.length);
  415.                         readFully(pos, buf, 0, n, curs);
  416.                         crc1.update(buf, 0, n);
  417.                         pos += n;
  418.                         cnt -= n;
  419.                     }
  420.                 }
  421.                 if (crc1.getValue() != expectedCRC) {
  422.                     setCorrupt(src.offset);
  423.                     throw new CorruptObjectException(MessageFormat.format(
  424.                             JGitText.get().objectAtHasBadZlibStream,
  425.                             Long.valueOf(src.offset), getPackFile()));
  426.                 }
  427.             } else if (validate) {
  428.                 // We don't have a CRC32 code in the index, so compute it
  429.                 // now while inflating the raw data to get zlib to tell us
  430.                 // whether or not the data is safe.
  431.                 //
  432.                 Inflater inf = curs.inflater();
  433.                 byte[] tmp = new byte[1024];
  434.                 if (quickCopy != null) {
  435.                     quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
  436.                 } else {
  437.                     assert(crc1 != null);
  438.                     long pos = dataOffset;
  439.                     long cnt = dataLength;
  440.                     while (cnt > 0) {
  441.                         final int n = (int) Math.min(cnt, buf.length);
  442.                         readFully(pos, buf, 0, n, curs);
  443.                         crc1.update(buf, 0, n);
  444.                         inf.setInput(buf, 0, n);
  445.                         while (inf.inflate(tmp, 0, tmp.length) > 0)
  446.                             continue;
  447.                         pos += n;
  448.                         cnt -= n;
  449.                     }
  450.                 }
  451.                 if (!inf.finished() || inf.getBytesRead() != dataLength) {
  452.                     setCorrupt(src.offset);
  453.                     throw new EOFException(MessageFormat.format(
  454.                             JGitText.get().shortCompressedStreamAt,
  455.                             Long.valueOf(src.offset)));
  456.                 }
  457.                 assert(crc1 != null);
  458.                 expectedCRC = crc1.getValue();
  459.             } else {
  460.                 expectedCRC = -1;
  461.             }
  462.         } catch (DataFormatException dataFormat) {
  463.             setCorrupt(src.offset);

  464.             CorruptObjectException corruptObject = new CorruptObjectException(
  465.                     MessageFormat.format(
  466.                             JGitText.get().objectAtHasBadZlibStream,
  467.                             Long.valueOf(src.offset), getPackFile()),
  468.                     dataFormat);

  469.             throw new StoredObjectRepresentationNotAvailableException(
  470.                     corruptObject);

  471.         } catch (IOException ioError) {
  472.             throw new StoredObjectRepresentationNotAvailableException(ioError);
  473.         }

  474.         if (quickCopy != null) {
  475.             // The entire object fits into a single byte array window slice,
  476.             // and we have it pinned.  Write this out without copying.
  477.             //
  478.             out.writeHeader(src, inflatedLength);
  479.             quickCopy.write(out, dataOffset, (int) dataLength);

  480.         } else if (dataLength <= buf.length) {
  481.             // Tiny optimization: Lots of objects are very small deltas or
  482.             // deflated commits that are likely to fit in the copy buffer.
  483.             //
  484.             if (!validate) {
  485.                 long pos = dataOffset;
  486.                 long cnt = dataLength;
  487.                 while (cnt > 0) {
  488.                     final int n = (int) Math.min(cnt, buf.length);
  489.                     readFully(pos, buf, 0, n, curs);
  490.                     pos += n;
  491.                     cnt -= n;
  492.                 }
  493.             }
  494.             out.writeHeader(src, inflatedLength);
  495.             out.write(buf, 0, (int) dataLength);
  496.         } else {
  497.             // Now we are committed to sending the object. As we spool it out,
  498.             // check its CRC32 code to make sure there wasn't corruption between
  499.             // the verification we did above, and us actually outputting it.
  500.             //
  501.             out.writeHeader(src, inflatedLength);
  502.             long pos = dataOffset;
  503.             long cnt = dataLength;
  504.             while (cnt > 0) {
  505.                 final int n = (int) Math.min(cnt, buf.length);
  506.                 readFully(pos, buf, 0, n, curs);
  507.                 if (validate) {
  508.                     assert(crc2 != null);
  509.                     crc2.update(buf, 0, n);
  510.                 }
  511.                 out.write(buf, 0, n);
  512.                 pos += n;
  513.                 cnt -= n;
  514.             }
  515.             if (validate) {
  516.                 assert(crc2 != null);
  517.                 if (crc2.getValue() != expectedCRC) {
  518.                     throw new CorruptObjectException(MessageFormat.format(
  519.                             JGitText.get().objectAtHasBadZlibStream,
  520.                             Long.valueOf(src.offset), getPackFile()));
  521.                 }
  522.             }
  523.         }
  524.     }

  525.     boolean invalid() {
  526.         return invalid;
  527.     }

  528.     void setInvalid() {
  529.         invalid = true;
  530.     }

  531.     int incrementTransientErrorCount() {
  532.         return transientErrorCount.incrementAndGet();
  533.     }

  534.     void resetTransientErrorCount() {
  535.         transientErrorCount.set(0);
  536.     }

  537.     private void readFully(final long position, final byte[] dstbuf,
  538.             int dstoff, final int cnt, final WindowCursor curs)
  539.             throws IOException {
  540.         if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
  541.             throw new EOFException();
  542.     }

  543.     private synchronized void beginCopyAsIs()
  544.             throws StoredObjectRepresentationNotAvailableException {
  545.         if (++activeCopyRawData == 1 && activeWindows == 0) {
  546.             try {
  547.                 doOpen();
  548.             } catch (IOException thisPackNotValid) {
  549.                 throw new StoredObjectRepresentationNotAvailableException(
  550.                         thisPackNotValid);
  551.             }
  552.         }
  553.     }

  554.     private synchronized void endCopyAsIs() {
  555.         if (--activeCopyRawData == 0 && activeWindows == 0)
  556.             doClose();
  557.     }

  558.     synchronized boolean beginWindowCache() throws IOException {
  559.         if (++activeWindows == 1) {
  560.             if (activeCopyRawData == 0)
  561.                 doOpen();
  562.             return true;
  563.         }
  564.         return false;
  565.     }

  566.     synchronized boolean endWindowCache() {
  567.         final boolean r = --activeWindows == 0;
  568.         if (r && activeCopyRawData == 0)
  569.             doClose();
  570.         return r;
  571.     }

  572.     private void doOpen() throws IOException {
  573.         if (invalid) {
  574.             openFail(true, invalidatingCause);
  575.             throw new PackInvalidException(packFile, invalidatingCause);
  576.         }
  577.         try {
  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, e);
  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(), fn);
  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, pe);
  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, ge);
  605.             throw ge;
  606.         }
  607.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  782.             if (data == null)
  783.                 throw new IOException(JGitText.get().inMemoryBufferLimitExceeded);

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

  791.                 pos = delta.deltaPos;

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

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

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

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

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

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

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

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

  832.         /** Offset of the delta object. */
  833.         final long deltaPos;

  834.         /** Size of the inflated delta stream. */
  835.         final int deltaSize;

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

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

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

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

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

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

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

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

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

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

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

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

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

  933.         case Constants.OBJ_REF_DELTA:
  934.             deltaAt = pos + p + 20;
  935.             break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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