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