diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index 77d60640b..90b2d2c42 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -17,12 +17,10 @@ import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.client.peer.SharingPeer; -import com.turn.ttorrent.client.storage.TorrentByteStorage; import java.io.IOException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.concurrent.Callable; import org.slf4j.Logger; @@ -52,12 +50,9 @@ public class Piece implements Comparable { private static final Logger logger = LoggerFactory.getLogger(Piece.class); - private final TorrentByteStorage bucket; + private final SharedTorrent torrent; private final int index; - private final long offset; private final long length; - private final byte[] hash; - private final boolean seeder; private volatile boolean valid; private int seen; @@ -66,22 +61,15 @@ public class Piece implements Comparable { /** * Initialize a new piece in the byte bucket. * - * @param bucket The underlying byte storage bucket. + * @param bucket The parent torrent. * @param index This piece index in the torrent. - * @param offset This piece offset, in bytes, in the storage. * @param length This piece length, in bytes. - * @param hash This piece 20-byte SHA1 hash sum. - * @param seeder Whether we're seeding this torrent or not (disables piece * validation). */ - public Piece(TorrentByteStorage bucket, int index, long offset, - long length, byte[] hash, boolean seeder) { - this.bucket = bucket; + public Piece(SharedTorrent torrent, int index, long length) { + this.torrent = torrent; this.index = index; - this.offset = offset; this.length = length; - this.hash = hash; - this.seeder = seeder; // Piece is considered invalid until first check. this.valid = false; @@ -150,7 +138,7 @@ public void noLongerAt(SharingPeer peer) { * meta-info. */ public synchronized boolean validate() throws IOException { - if (this.seeder) { + if (this.torrent.isSeeder()) { logger.trace("Skipping validation of {} (seeder mode).", this); this.valid = true; return true; @@ -160,18 +148,28 @@ public synchronized boolean validate() throws IOException { this.valid = false; try { - // TODO: remove cast to int when large ByteBuffer support is - // implemented in Java. ByteBuffer buffer = this._read(0, this.length); - byte[] data = new byte[(int)this.length]; - buffer.get(data); - this.valid = Arrays.equals(Torrent.hash(data), this.hash); + this.valid = checkHash(buffer); } catch (NoSuchAlgorithmException nsae) { logger.error("{}", nsae); } return this.isValid(); } + + protected boolean checkHash(ByteBuffer data) throws NoSuchAlgorithmException { + byte[] calculatedHash = Torrent.hash(data); + + int torrentHashPosition = getIndex() * Torrent.PIECE_HASH_SIZE; + ByteBuffer torrentHash = this.torrent.getPiecesHashes(); + for (int i = 0; i < Torrent.PIECE_HASH_SIZE; i++) { + byte value = torrentHash.get(torrentHashPosition + i); + if (value != calculatedHash[i]) { + return false; + } + } + return true; + } /** * Internal piece data read function. @@ -200,7 +198,7 @@ private ByteBuffer _read(long offset, long length) throws IOException { // TODO: remove cast to int when large ByteBuffer support is // implemented in Java. ByteBuffer buffer = ByteBuffer.allocate((int)length); - int bytes = this.bucket.read(buffer, this.offset + offset); + int bytes = this.torrent.getBucket().read(buffer, this.getBucketOffset() + offset); buffer.rewind(); buffer.limit(bytes >= 0 ? bytes : 0); return buffer; @@ -262,11 +260,15 @@ public synchronized void record(ByteBuffer block, int offset) if (block.remaining() + offset == this.length) { this.data.rewind(); logger.trace("Recording {}...", this); - this.bucket.write(this.data, this.offset); + this.torrent.getBucket().write(this.data, this.getBucketOffset()); this.data = null; } } + long getBucketOffset() { + return ((long)this.index) * this.torrent.getPieceLength(); + } + /** * Return a human-readable representation of this piece. */ diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index a040d3d50..ba44b7156 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -321,8 +321,6 @@ public synchronized void init() throws InterruptedException, IOException { logger.info("Analyzing local data for {} with {} threads ({} pieces)...", new Object[] { this.getName(), threads, nPieces }); for (int idx=0; idx hasher = new Piece.CallableHasher(this.pieces[idx]); results.add(executor.submit(hasher)); @@ -835,4 +832,25 @@ public synchronized void handlePeerDisconnected(SharingPeer peer) { @Override public synchronized void handleIOException(SharingPeer peer, IOException ioe) { /* Do nothing */ } + + + /** + * For accessing from Piece. + * @return + */ + TorrentByteStorage getBucket() { + return bucket; + } + + /** + * For accessing from Piece. + * @return + */ + int getPieceLength() { + return pieceLength; + } + + ByteBuffer getPiecesHashes() { + return piecesHashes; + } } diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 0b3194ae7..e01057af8 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -418,6 +418,12 @@ public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { return md.digest(); } + public static byte[] hash(ByteBuffer data) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(data); + return md.digest(); + } + /** * Convert a byte string to a string containing an hexadecimal * representation of the original data.