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.

DfsPackParser.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. * Copyright (C) 2011, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.internal.storage.dfs;
  44. import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
  45. import java.io.EOFException;
  46. import java.io.IOException;
  47. import java.io.InputStream;
  48. import java.nio.ByteBuffer;
  49. import java.security.MessageDigest;
  50. import java.util.Collections;
  51. import java.util.List;
  52. import java.util.zip.CRC32;
  53. import java.util.zip.Deflater;
  54. import org.eclipse.jgit.internal.storage.file.PackIndex;
  55. import org.eclipse.jgit.internal.storage.file.PackLock;
  56. import org.eclipse.jgit.lib.AnyObjectId;
  57. import org.eclipse.jgit.lib.Constants;
  58. import org.eclipse.jgit.lib.ProgressMonitor;
  59. import org.eclipse.jgit.transport.PackParser;
  60. import org.eclipse.jgit.transport.PackedObjectInfo;
  61. /**
  62. * Parses a pack stream into the DFS, by creating a new pack and index.
  63. */
  64. public class DfsPackParser extends PackParser {
  65. private final DfsObjDatabase objdb;
  66. private final DfsInserter objins;
  67. /** CRC-32 computation for objects that are appended onto the pack. */
  68. private final CRC32 crc;
  69. /** Running SHA-1 of the entire pack stream. */
  70. private final MessageDigest packDigest;
  71. /** Block size to use when caching data for read back. */
  72. private int blockSize;
  73. /** Current end of the pack file. */
  74. private long packEnd;
  75. /** Checksum of the entire pack file. */
  76. private byte[] packHash;
  77. /** Compresses delta bases when completing a thin pack. */
  78. private Deflater def;
  79. /** True if the pack is an empty pack. */
  80. private boolean isEmptyPack;
  81. /** Name of the pack file, computed in {@link #onPackHeader(long)}. */
  82. private DfsPackDescription packDsc;
  83. /** Key used during delta resolution reading delta chains. */
  84. private DfsStreamKey packKey;
  85. /** If the index was small enough, the entire index after writing. */
  86. private PackIndex packIndex;
  87. /** Stream to the DFS storage, opened during {@link #onPackHeader(long)}. */
  88. private DfsOutputStream out;
  89. /** Data being written that has not yet been cached. */
  90. private byte[] currBuf;
  91. private long currPos; // Position of currBuf in the file.
  92. private int currEnd; // Position inside of currBuf to append to next.
  93. /** Cache the chunks were stored into or get read back from. */
  94. private DfsBlockCache blockCache;
  95. /** Cached block that is being read. */
  96. private long readPos;
  97. private DfsBlock readBlock;
  98. /**
  99. * Initialize a new pack parser.
  100. *
  101. * @param db
  102. * database the objects will be imported into.
  103. * @param ins
  104. * inserter the parser will use to help it inject the objects.
  105. * @param in
  106. * the stream to parse.
  107. */
  108. protected DfsPackParser(DfsObjDatabase db, DfsInserter ins, InputStream in) {
  109. super(db, in);
  110. this.objdb = db;
  111. this.objins = ins;
  112. this.crc = new CRC32();
  113. this.packDigest = Constants.newMessageDigest();
  114. }
  115. /** {@inheritDoc} */
  116. @Override
  117. public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
  118. throws IOException {
  119. boolean rollback = true;
  120. try {
  121. blockCache = DfsBlockCache.getInstance();
  122. super.parse(receiving, resolving);
  123. if (isEmptyPack)
  124. return null;
  125. buffer(packHash, 0, packHash.length);
  126. if (currEnd != 0)
  127. flushBlock();
  128. out.close();
  129. out = null;
  130. currBuf = null;
  131. readBlock = null;
  132. packDsc.addFileExt(PACK);
  133. packDsc.setFileSize(PACK, packEnd);
  134. packDsc.setBlockSize(PACK, blockSize);
  135. writePackIndex();
  136. objdb.commitPack(Collections.singletonList(packDsc), null);
  137. rollback = false;
  138. DfsPackFile p = new DfsPackFile(blockCache, packDsc);
  139. p.setBlockSize(blockSize);
  140. if (packIndex != null)
  141. p.setPackIndex(packIndex);
  142. objdb.addPack(p);
  143. return null;
  144. } finally {
  145. blockCache = null;
  146. currBuf = null;
  147. readBlock = null;
  148. if (def != null) {
  149. def.end();
  150. def = null;
  151. }
  152. if (out != null) {
  153. try {
  154. out.close();
  155. } catch (IOException err) {
  156. // Ignore a close error, rollbackPack is also important.
  157. }
  158. out = null;
  159. }
  160. if (rollback && packDsc != null) {
  161. try {
  162. objdb.rollbackPack(Collections.singletonList(packDsc));
  163. } finally {
  164. packDsc = null;
  165. }
  166. }
  167. }
  168. }
  169. /**
  170. * Get description of the imported pack, if one was made.
  171. *
  172. * @return description of the imported pack, if one was made.
  173. */
  174. public DfsPackDescription getPackDescription() {
  175. return packDsc;
  176. }
  177. /** {@inheritDoc} */
  178. @Override
  179. protected void onPackHeader(long objectCount) throws IOException {
  180. if (objectCount == 0) {
  181. isEmptyPack = true;
  182. currBuf = new byte[256];
  183. return;
  184. }
  185. packDsc = objdb.newPack(DfsObjDatabase.PackSource.RECEIVE);
  186. out = objdb.writeFile(packDsc, PACK);
  187. packKey = packDsc.getStreamKey(PACK);
  188. int size = out.blockSize();
  189. if (size <= 0)
  190. size = blockCache.getBlockSize();
  191. else if (size < blockCache.getBlockSize())
  192. size = (blockCache.getBlockSize() / size) * size;
  193. blockSize = size;
  194. currBuf = new byte[blockSize];
  195. }
  196. /** {@inheritDoc} */
  197. @Override
  198. protected void onBeginWholeObject(long streamPosition, int type,
  199. long inflatedSize) throws IOException {
  200. crc.reset();
  201. }
  202. /** {@inheritDoc} */
  203. @Override
  204. protected void onEndWholeObject(PackedObjectInfo info) throws IOException {
  205. info.setCRC((int) crc.getValue());
  206. }
  207. /** {@inheritDoc} */
  208. @Override
  209. protected void onBeginOfsDelta(long streamPosition,
  210. long baseStreamPosition, long inflatedSize) throws IOException {
  211. crc.reset();
  212. }
  213. /** {@inheritDoc} */
  214. @Override
  215. protected void onBeginRefDelta(long streamPosition, AnyObjectId baseId,
  216. long inflatedSize) throws IOException {
  217. crc.reset();
  218. }
  219. /** {@inheritDoc} */
  220. @Override
  221. protected UnresolvedDelta onEndDelta() throws IOException {
  222. UnresolvedDelta delta = new UnresolvedDelta();
  223. delta.setCRC((int) crc.getValue());
  224. return delta;
  225. }
  226. /** {@inheritDoc} */
  227. @Override
  228. protected void onInflatedObjectData(PackedObjectInfo obj, int typeCode,
  229. byte[] data) throws IOException {
  230. // DfsPackParser ignores this event.
  231. }
  232. /** {@inheritDoc} */
  233. @Override
  234. protected void onObjectHeader(Source src, byte[] raw, int pos, int len)
  235. throws IOException {
  236. crc.update(raw, pos, len);
  237. }
  238. /** {@inheritDoc} */
  239. @Override
  240. protected void onObjectData(Source src, byte[] raw, int pos, int len)
  241. throws IOException {
  242. crc.update(raw, pos, len);
  243. }
  244. /** {@inheritDoc} */
  245. @Override
  246. protected void onStoreStream(byte[] raw, int pos, int len)
  247. throws IOException {
  248. buffer(raw, pos, len);
  249. packDigest.update(raw, pos, len);
  250. }
  251. private void buffer(byte[] raw, int pos, int len) throws IOException {
  252. while (0 < len) {
  253. int n = Math.min(len, currBuf.length - currEnd);
  254. if (n == 0) {
  255. DfsBlock v = flushBlock();
  256. currBuf = new byte[blockSize];
  257. currEnd = 0;
  258. currPos += v.size();
  259. continue;
  260. }
  261. System.arraycopy(raw, pos, currBuf, currEnd, n);
  262. pos += n;
  263. len -= n;
  264. currEnd += n;
  265. packEnd += n;
  266. }
  267. }
  268. private DfsBlock flushBlock() throws IOException {
  269. if (isEmptyPack)
  270. throw new IOException(DfsText.get().willNotStoreEmptyPack);
  271. out.write(currBuf, 0, currEnd);
  272. byte[] buf;
  273. if (currEnd == currBuf.length) {
  274. buf = currBuf;
  275. } else {
  276. buf = new byte[currEnd];
  277. System.arraycopy(currBuf, 0, buf, 0, currEnd);
  278. }
  279. DfsBlock v = new DfsBlock(packKey, currPos, buf);
  280. readBlock = v;
  281. blockCache.put(v);
  282. return v;
  283. }
  284. /** {@inheritDoc} */
  285. @Override
  286. protected void onPackFooter(byte[] hash) throws IOException {
  287. // The base class will validate the original hash matches
  288. // what the stream has stored at the end. We are called
  289. // only if the hash was good. Save it in case there are no
  290. // missing bases to append.
  291. packHash = hash;
  292. }
  293. /** {@inheritDoc} */
  294. @Override
  295. protected ObjectTypeAndSize seekDatabase(PackedObjectInfo obj,
  296. ObjectTypeAndSize info) throws IOException {
  297. readPos = obj.getOffset();
  298. crc.reset();
  299. return readObjectHeader(info);
  300. }
  301. /** {@inheritDoc} */
  302. @Override
  303. protected ObjectTypeAndSize seekDatabase(UnresolvedDelta delta,
  304. ObjectTypeAndSize info) throws IOException {
  305. readPos = delta.getOffset();
  306. crc.reset();
  307. return readObjectHeader(info);
  308. }
  309. /** {@inheritDoc} */
  310. @Override
  311. protected int readDatabase(byte[] dst, int pos, int cnt) throws IOException {
  312. if (cnt == 0)
  313. return 0;
  314. if (currPos <= readPos) {
  315. // Requested read is still buffered. Copy direct from buffer.
  316. int p = (int) (readPos - currPos);
  317. int n = Math.min(cnt, currEnd - p);
  318. if (n == 0)
  319. throw new EOFException();
  320. System.arraycopy(currBuf, p, dst, pos, n);
  321. readPos += n;
  322. return n;
  323. }
  324. if (readBlock == null || !readBlock.contains(packKey, readPos)) {
  325. long start = toBlockStart(readPos);
  326. readBlock = blockCache.get(packKey, start);
  327. if (readBlock == null) {
  328. int size = (int) Math.min(blockSize, packEnd - start);
  329. byte[] buf = new byte[size];
  330. if (read(start, buf, 0, size) != size)
  331. throw new EOFException();
  332. readBlock = new DfsBlock(packKey, start, buf);
  333. blockCache.put(readBlock);
  334. }
  335. }
  336. int n = readBlock.copy(readPos, dst, pos, cnt);
  337. readPos += n;
  338. return n;
  339. }
  340. private int read(long pos, byte[] dst, int off, int len) throws IOException {
  341. if (len == 0)
  342. return 0;
  343. int cnt = 0;
  344. while (0 < len) {
  345. int r = out.read(pos, ByteBuffer.wrap(dst, off, len));
  346. if (r <= 0)
  347. break;
  348. pos += r;
  349. off += r;
  350. len -= r;
  351. cnt += r;
  352. }
  353. return cnt != 0 ? cnt : -1;
  354. }
  355. private long toBlockStart(long pos) {
  356. return (pos / blockSize) * blockSize;
  357. }
  358. /** {@inheritDoc} */
  359. @Override
  360. protected boolean checkCRC(int oldCRC) {
  361. return oldCRC == (int) crc.getValue();
  362. }
  363. /** {@inheritDoc} */
  364. @Override
  365. protected boolean onAppendBase(final int typeCode, final byte[] data,
  366. final PackedObjectInfo info) throws IOException {
  367. info.setOffset(packEnd);
  368. final byte[] buf = buffer();
  369. int sz = data.length;
  370. int len = 0;
  371. buf[len++] = (byte) ((typeCode << 4) | (sz & 15));
  372. sz >>>= 4;
  373. while (sz > 0) {
  374. buf[len - 1] |= (byte) 0x80;
  375. buf[len++] = (byte) (sz & 0x7f);
  376. sz >>>= 7;
  377. }
  378. packDigest.update(buf, 0, len);
  379. crc.reset();
  380. crc.update(buf, 0, len);
  381. buffer(buf, 0, len);
  382. if (def == null)
  383. def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
  384. else
  385. def.reset();
  386. def.setInput(data);
  387. def.finish();
  388. while (!def.finished()) {
  389. len = def.deflate(buf);
  390. packDigest.update(buf, 0, len);
  391. crc.update(buf, 0, len);
  392. buffer(buf, 0, len);
  393. }
  394. info.setCRC((int) crc.getValue());
  395. return true;
  396. }
  397. /** {@inheritDoc} */
  398. @Override
  399. protected void onEndThinPack() throws IOException {
  400. // Normally when a thin pack is closed the pack header gets
  401. // updated to reflect the actual object count. This is not going
  402. // to be possible on most DFS backends, so instead we allow
  403. // the header to have an incorrect count, but we do change the
  404. // trailing digest to be correct.
  405. packHash = packDigest.digest();
  406. }
  407. private void writePackIndex() throws IOException {
  408. List<PackedObjectInfo> list = getSortedObjectList(null /* by ObjectId */);
  409. packIndex = objins.writePackIndex(packDsc, packHash, list);
  410. }
  411. }