You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Pack.java 33KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  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. }