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.

ObjectDirectoryInserter.java 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * Copyright (C) 2009, Google Inc. 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 java.io.EOFException;
  14. import java.io.File;
  15. import java.io.FileOutputStream;
  16. import java.io.FilterOutputStream;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.nio.channels.Channels;
  21. import java.text.MessageFormat;
  22. import java.util.zip.Deflater;
  23. import java.util.zip.DeflaterOutputStream;
  24. import org.eclipse.jgit.errors.ObjectWritingException;
  25. import org.eclipse.jgit.internal.JGitText;
  26. import org.eclipse.jgit.lib.Config;
  27. import org.eclipse.jgit.lib.Constants;
  28. import org.eclipse.jgit.lib.ObjectId;
  29. import org.eclipse.jgit.lib.ObjectInserter;
  30. import org.eclipse.jgit.lib.ObjectReader;
  31. import org.eclipse.jgit.transport.PackParser;
  32. import org.eclipse.jgit.util.FileUtils;
  33. import org.eclipse.jgit.util.IO;
  34. import org.eclipse.jgit.util.sha1.SHA1;
  35. /** Creates loose objects in a {@link ObjectDirectory}. */
  36. class ObjectDirectoryInserter extends ObjectInserter {
  37. private final FileObjectDatabase db;
  38. private final WriteConfig config;
  39. private Deflater deflate;
  40. ObjectDirectoryInserter(FileObjectDatabase dest, Config cfg) {
  41. db = dest;
  42. config = cfg.get(WriteConfig.KEY);
  43. }
  44. /** {@inheritDoc} */
  45. @Override
  46. public ObjectId insert(int type, byte[] data, int off, int len)
  47. throws IOException {
  48. return insert(type, data, off, len, false);
  49. }
  50. /**
  51. * Insert a loose object into the database. If createDuplicate is true,
  52. * write the loose object even if we already have it in the loose or packed
  53. * ODB.
  54. *
  55. * @param type
  56. * @param data
  57. * @param off
  58. * @param len
  59. * @param createDuplicate
  60. * @return ObjectId
  61. * @throws IOException
  62. */
  63. private ObjectId insert(
  64. int type, byte[] data, int off, int len, boolean createDuplicate)
  65. throws IOException {
  66. ObjectId id = idFor(type, data, off, len);
  67. if (!createDuplicate && db.has(id)) {
  68. return id;
  69. }
  70. File tmp = toTemp(type, data, off, len);
  71. return insertOneObject(tmp, id, createDuplicate);
  72. }
  73. /** {@inheritDoc} */
  74. @Override
  75. public ObjectId insert(int type, long len, InputStream is)
  76. throws IOException {
  77. return insert(type, len, is, false);
  78. }
  79. /**
  80. * Insert a loose object into the database. If createDuplicate is true,
  81. * write the loose object even if we already have it in the loose or packed
  82. * ODB.
  83. *
  84. * @param type
  85. * @param len
  86. * @param is
  87. * @param createDuplicate
  88. * @return ObjectId
  89. * @throws IOException
  90. */
  91. ObjectId insert(int type, long len, InputStream is, boolean createDuplicate)
  92. throws IOException {
  93. if (len <= buffer().length) {
  94. byte[] buf = buffer();
  95. int actLen = IO.readFully(is, buf, 0);
  96. return insert(type, buf, 0, actLen, createDuplicate);
  97. }
  98. SHA1 md = digest();
  99. File tmp = toTemp(md, type, len, is);
  100. ObjectId id = md.toObjectId();
  101. return insertOneObject(tmp, id, createDuplicate);
  102. }
  103. private ObjectId insertOneObject(
  104. File tmp, ObjectId id, boolean createDuplicate)
  105. throws IOException {
  106. switch (db.insertUnpackedObject(tmp, id, createDuplicate)) {
  107. case INSERTED:
  108. case EXISTS_PACKED:
  109. case EXISTS_LOOSE:
  110. return id;
  111. case FAILURE:
  112. default:
  113. break;
  114. }
  115. final File dst = db.fileFor(id);
  116. throw new ObjectWritingException(MessageFormat
  117. .format(JGitText.get().unableToCreateNewObject, dst));
  118. }
  119. /** {@inheritDoc} */
  120. @Override
  121. public PackParser newPackParser(InputStream in) throws IOException {
  122. return new ObjectDirectoryPackParser(db, in);
  123. }
  124. /** {@inheritDoc} */
  125. @Override
  126. public ObjectReader newReader() {
  127. return new WindowCursor(db, this);
  128. }
  129. /** {@inheritDoc} */
  130. @Override
  131. public void flush() throws IOException {
  132. // Do nothing. Loose objects are immediately visible.
  133. }
  134. /** {@inheritDoc} */
  135. @Override
  136. public void close() {
  137. if (deflate != null) {
  138. try {
  139. deflate.end();
  140. } finally {
  141. deflate = null;
  142. }
  143. }
  144. }
  145. @SuppressWarnings("resource" /* java 7 */)
  146. private File toTemp(final SHA1 md, final int type, long len,
  147. final InputStream is) throws IOException {
  148. boolean delete = true;
  149. File tmp = newTempFile();
  150. try {
  151. FileOutputStream fOut = new FileOutputStream(tmp);
  152. try {
  153. OutputStream out = fOut;
  154. if (config.getFSyncObjectFiles())
  155. out = Channels.newOutputStream(fOut.getChannel());
  156. DeflaterOutputStream cOut = compress(out);
  157. SHA1OutputStream dOut = new SHA1OutputStream(cOut, md);
  158. writeHeader(dOut, type, len);
  159. final byte[] buf = buffer();
  160. while (len > 0) {
  161. int n = is.read(buf, 0, (int) Math.min(len, buf.length));
  162. if (n <= 0)
  163. throw shortInput(len);
  164. dOut.write(buf, 0, n);
  165. len -= n;
  166. }
  167. dOut.flush();
  168. cOut.finish();
  169. } finally {
  170. if (config.getFSyncObjectFiles())
  171. fOut.getChannel().force(true);
  172. fOut.close();
  173. }
  174. delete = false;
  175. return tmp;
  176. } finally {
  177. if (delete)
  178. FileUtils.delete(tmp, FileUtils.RETRY);
  179. }
  180. }
  181. @SuppressWarnings("resource" /* java 7 */)
  182. private File toTemp(final int type, final byte[] buf, final int pos,
  183. final int len) throws IOException {
  184. boolean delete = true;
  185. File tmp = newTempFile();
  186. try {
  187. FileOutputStream fOut = new FileOutputStream(tmp);
  188. try {
  189. OutputStream out = fOut;
  190. if (config.getFSyncObjectFiles())
  191. out = Channels.newOutputStream(fOut.getChannel());
  192. DeflaterOutputStream cOut = compress(out);
  193. writeHeader(cOut, type, len);
  194. cOut.write(buf, pos, len);
  195. cOut.finish();
  196. } finally {
  197. if (config.getFSyncObjectFiles())
  198. fOut.getChannel().force(true);
  199. fOut.close();
  200. }
  201. delete = false;
  202. return tmp;
  203. } finally {
  204. if (delete)
  205. FileUtils.delete(tmp, FileUtils.RETRY);
  206. }
  207. }
  208. void writeHeader(OutputStream out, int type, long len)
  209. throws IOException {
  210. out.write(Constants.encodedTypeString(type));
  211. out.write((byte) ' ');
  212. out.write(Constants.encodeASCII(len));
  213. out.write((byte) 0);
  214. }
  215. File newTempFile() throws IOException {
  216. return File.createTempFile("noz", null, db.getDirectory()); //$NON-NLS-1$
  217. }
  218. DeflaterOutputStream compress(OutputStream out) {
  219. if (deflate == null)
  220. deflate = new Deflater(config.getCompression());
  221. else
  222. deflate.reset();
  223. return new DeflaterOutputStream(out, deflate, 8192);
  224. }
  225. private static EOFException shortInput(long missing) {
  226. return new EOFException(MessageFormat.format(
  227. JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
  228. }
  229. private static class SHA1OutputStream extends FilterOutputStream {
  230. private final SHA1 md;
  231. SHA1OutputStream(OutputStream out, SHA1 md) {
  232. super(out);
  233. this.md = md;
  234. }
  235. @Override
  236. public void write(int b) throws IOException {
  237. md.update((byte) b);
  238. out.write(b);
  239. }
  240. @Override
  241. public void write(byte[] in, int p, int n) throws IOException {
  242. md.update(in, p, n);
  243. out.write(in, p, n);
  244. }
  245. }
  246. }