diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java index 6b746e319..731b4caa8 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java @@ -64,7 +64,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.util.IO; /** * Dumps a file over HTTP GET (or its information via HEAD). @@ -122,7 +121,8 @@ long getLastModified() { String getTailChecksum() throws IOException { final int n = 20; final byte[] buf = new byte[n]; - IO.readFully(source.getChannel(), fileLen - n, buf, 0, n); + source.seek(fileLen - n); + source.readFully(buf, 0, n); return ObjectId.fromRaw(buf).getName(); } @@ -140,6 +140,7 @@ void serve(final HttpServletRequest req, final HttpServletResponse rsp, final OutputStream out = rsp.getOutputStream(); try { final byte[] buf = new byte[4096]; + source.seek(pos); while (pos < end) { final int r = (int) Math.min(buf.length, end - pos); final int n = source.read(buf, 0, r); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java index 8f4e69163..829832e6a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java @@ -65,7 +65,6 @@ import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.PackInvalidException; import org.eclipse.jgit.errors.PackMismatchException; -import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.NB; import org.eclipse.jgit.util.RawParseUtils; @@ -90,6 +89,9 @@ public int compare(final PackFile a, final PackFile b) { private RandomAccessFile fd; + /** Serializes reads performed against {@link #fd}. */ + private final Object readLock = new Object(); + long length; private int activeWindows; @@ -364,9 +366,11 @@ private void doOpen() throws IOException { try { if (invalid) throw new PackInvalidException(packFile); - fd = new RandomAccessFile(packFile, "r"); - length = fd.length(); - onOpenPack(); + synchronized (readLock) { + fd = new RandomAccessFile(packFile, "r"); + length = fd.length(); + onOpenPack(); + } } catch (IOException ioe) { openFail(); throw ioe; @@ -387,53 +391,61 @@ private void openFail() { } private void doClose() { - if (fd != null) { - try { - fd.close(); - } catch (IOException err) { - // Ignore a close event. We had it open only for reading. - // There should not be errors related to network buffers - // not flushed, etc. + synchronized (readLock) { + if (fd != null) { + try { + fd.close(); + } catch (IOException err) { + // Ignore a close event. We had it open only for reading. + // There should not be errors related to network buffers + // not flushed, etc. + } + fd = null; } - fd = null; } } ByteArrayWindow read(final long pos, int size) throws IOException { - if (length < pos + size) - size = (int) (length - pos); - final byte[] buf = new byte[size]; - IO.readFully(fd.getChannel(), pos, buf, 0, size); - return new ByteArrayWindow(this, pos, buf); + synchronized (readLock) { + if (length < pos + size) + size = (int) (length - pos); + final byte[] buf = new byte[size]; + fd.seek(pos); + fd.readFully(buf, 0, size); + return new ByteArrayWindow(this, pos, buf); + } } ByteWindow mmap(final long pos, int size) throws IOException { - if (length < pos + size) - size = (int) (length - pos); + synchronized (readLock) { + if (length < pos + size) + size = (int) (length - pos); - MappedByteBuffer map; - try { - map = fd.getChannel().map(MapMode.READ_ONLY, pos, size); - } catch (IOException ioe1) { - // The most likely reason this failed is the JVM has run out - // of virtual memory. We need to discard quickly, and try to - // force the GC to finalize and release any existing mappings. - // - System.gc(); - System.runFinalization(); - map = fd.getChannel().map(MapMode.READ_ONLY, pos, size); + MappedByteBuffer map; + try { + map = fd.getChannel().map(MapMode.READ_ONLY, pos, size); + } catch (IOException ioe1) { + // The most likely reason this failed is the JVM has run out + // of virtual memory. We need to discard quickly, and try to + // force the GC to finalize and release any existing mappings. + // + System.gc(); + System.runFinalization(); + map = fd.getChannel().map(MapMode.READ_ONLY, pos, size); + } + + if (map.hasArray()) + return new ByteArrayWindow(this, pos, map.array()); + return new ByteBufferWindow(this, pos, map); } - - if (map.hasArray()) - return new ByteArrayWindow(this, pos, map.array()); - return new ByteBufferWindow(this, pos, map); } private void onOpenPack() throws IOException { final PackIndex idx = idx(); final byte[] buf = new byte[20]; - IO.readFully(fd.getChannel(), 0, buf, 0, 12); + fd.seek(0); + fd.readFully(buf, 0, 12); if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4) throw new IOException(JGitText.get().notAPACKFile); final long vers = NB.decodeUInt32(buf, 4); @@ -445,7 +457,8 @@ private void onOpenPack() throws IOException { throw new PackMismatchException(MessageFormat.format( JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile())); - IO.readFully(fd.getChannel(), length - 20, buf, 0, 20); + fd.seek(length - 20); + fd.read(buf, 0, 20); if (!Arrays.equals(buf, packChecksum)) throw new PackMismatchException(MessageFormat.format( JGitText.get().packObjectCountMismatch diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java index 177865420..1f2042d4c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java @@ -51,8 +51,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; import java.text.MessageFormat; import org.eclipse.jgit.JGitText; @@ -138,36 +136,6 @@ public static void readFully(final InputStream fd, final byte[] dst, } } - /** - * Read the entire byte array into memory, or throw an exception. - * - * @param fd - * file to read the data from. - * @param pos - * position to read from the file at. - * @param dst - * buffer that must be fully populated, [off, off+len). - * @param off - * position within the buffer to start writing to. - * @param len - * number of bytes that must be read. - * @throws EOFException - * the stream ended before dst was fully populated. - * @throws IOException - * there was an error reading from the stream. - */ - public static void readFully(final FileChannel fd, long pos, - final byte[] dst, int off, int len) throws IOException { - while (len > 0) { - final int r = fd.read(ByteBuffer.wrap(dst, off, len), pos); - if (r <= 0) - throw new EOFException(JGitText.get().shortReadOfBlock); - pos += r; - off += r; - len -= r; - } - } - /** * Skip an entire region of an input stream. *