forked from I2P_Developers/i2p.i2p
* i2psnark:
- Add allocating and checking indications - Add bandwidth message at startup - More checks at torrent creation
This commit is contained in:
@ -787,7 +787,8 @@ class PeerCoordinator implements PeerListener
|
||||
}
|
||||
if (record) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(peer + " is now requesting: piece " + piece + " priority " + piece.getPriority());
|
||||
_log.info("Now requesting from " + peer + ": piece " + piece + " priority " + piece.getPriority() +
|
||||
" peers " + piece.getPeerCount() + '/' + peers.size());
|
||||
piece.setRequested(peer, true);
|
||||
}
|
||||
return piece;
|
||||
|
@ -688,6 +688,22 @@ public class Snark
|
||||
starting = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isChecking() {
|
||||
return storage != null && storage.isChecking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isAllocating() {
|
||||
return storage != null && storage.isAllocating();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.4
|
||||
*/
|
||||
|
@ -707,7 +707,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public Properties getConfig() { return _config; }
|
||||
|
||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
||||
private static final int MAX_FILES_PER_TORRENT = 512;
|
||||
public static final int MAX_FILES_PER_TORRENT = 512;
|
||||
|
||||
/**
|
||||
* Set of canonical .torrent filenames that we are dealing with.
|
||||
@ -1370,6 +1370,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
} catch (Exception e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
}
|
||||
if (!_snarks.isEmpty())
|
||||
addMessage(_("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
}
|
||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.crypto.SHA1;
|
||||
import net.i2p.data.ByteArray;
|
||||
@ -45,7 +46,7 @@ import net.i2p.util.SystemVersion;
|
||||
*/
|
||||
public class Storage
|
||||
{
|
||||
private MetaInfo metainfo;
|
||||
private final MetaInfo metainfo;
|
||||
private long[] lengths;
|
||||
private RandomAccessFile[] rafs;
|
||||
private String[] names;
|
||||
@ -69,6 +70,8 @@ public class Storage
|
||||
private final int pieces;
|
||||
private final long total_length;
|
||||
private boolean changed;
|
||||
private volatile boolean _isChecking;
|
||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||
|
||||
/** The default piece size. */
|
||||
private static final int MIN_PIECE_SIZE = 256*1024;
|
||||
@ -89,17 +92,15 @@ public class Storage
|
||||
* Creates a new storage based on the supplied MetaInfo. This will
|
||||
* try to create and/or check all needed files in the MetaInfo.
|
||||
*
|
||||
* @exception IOException when creating and/or checking files fails.
|
||||
* Does not check storage. Caller MUST call check()
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
|
||||
throws IOException
|
||||
{
|
||||
_util = util;
|
||||
_log = util.getContext().logManager().getLog(Storage.class);
|
||||
this.metainfo = metainfo;
|
||||
this.listener = listener;
|
||||
needed = metainfo.getPieces();
|
||||
_probablyComplete = false;
|
||||
bitfield = new BitField(needed);
|
||||
piece_size = metainfo.getPieceLength(0);
|
||||
pieces = needed;
|
||||
@ -107,12 +108,15 @@ public class Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a storage from the existing file or directory together
|
||||
* with an appropriate MetaInfo file as can be announced on the
|
||||
* given announce String location.
|
||||
* Creates a storage from the existing file or directory.
|
||||
* Creates an in-memory metainfo but does not save it to
|
||||
* a file, caller must do that.
|
||||
*
|
||||
* Creates the metainfo, this may take a LONG time. BLOCKING.
|
||||
*
|
||||
* @param announce may be null
|
||||
* @param listener may be null
|
||||
* @throws IOException when creating and/or checking files fails.
|
||||
*/
|
||||
public Storage(I2PSnarkUtil util, File baseFile, String announce,
|
||||
boolean privateTorrent, StorageListener listener)
|
||||
@ -135,6 +139,8 @@ public class Storage
|
||||
|
||||
if (total <= 0)
|
||||
throw new IOException("Torrent contains no data");
|
||||
if (total > MAX_TOTAL_SIZE)
|
||||
throw new IOException("Torrent too big (" + total + " bytes), max is " + MAX_TOTAL_SIZE);
|
||||
|
||||
int pc_size = MIN_PIECE_SIZE;
|
||||
int pcs = (int) ((total - 1)/pc_size) + 1;
|
||||
@ -170,6 +176,7 @@ public class Storage
|
||||
lengthsList = null;
|
||||
}
|
||||
|
||||
// TODO thread this so we can return and show something on the UI
|
||||
byte[] piece_hashes = fast_digestCreate();
|
||||
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
|
||||
lengthsList, piece_size, piece_hashes, total, privateTorrent);
|
||||
@ -205,6 +212,8 @@ public class Storage
|
||||
|
||||
private void getFiles(File base) throws IOException
|
||||
{
|
||||
if (base.getAbsolutePath().equals("/"))
|
||||
throw new IOException("Don't seed root");
|
||||
ArrayList files = new ArrayList();
|
||||
addFiles(files, base);
|
||||
|
||||
@ -233,12 +242,15 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
private void addFiles(List l, File f)
|
||||
{
|
||||
if (!f.isDirectory())
|
||||
l.add(f);
|
||||
else
|
||||
{
|
||||
/**
|
||||
* @throws IOException if too many total files
|
||||
*/
|
||||
private void addFiles(List l, File f) throws IOException {
|
||||
if (!f.isDirectory()) {
|
||||
if (l.size() >= SnarkManager.MAX_FILES_PER_TORRENT)
|
||||
throw new IOException("Too many files, limit is " + SnarkManager.MAX_FILES_PER_TORRENT + ", zip them?");
|
||||
l.add(f);
|
||||
} else {
|
||||
File[] files = f.listFiles();
|
||||
if (files == null)
|
||||
{
|
||||
@ -284,6 +296,23 @@ public class Storage
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isChecking() {
|
||||
return _isChecking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disk allocation (ballooning) in progress.
|
||||
* Always false on Windows.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public boolean isAllocating() {
|
||||
return _allocateCount.get() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @return number of bytes remaining; -1 if unknown file
|
||||
@ -703,11 +732,25 @@ public class Storage
|
||||
* This is called at the beginning, and at presumed completion,
|
||||
* so we have to be careful about locking.
|
||||
*
|
||||
* TODO thread the checking so we can return and display
|
||||
* something on the UI
|
||||
*
|
||||
* @param recheck if true, this is a check after we downloaded the
|
||||
* last piece, and we don't modify the global bitfield unless
|
||||
* the check fails.
|
||||
*/
|
||||
private void checkCreateFiles(boolean recheck) throws IOException
|
||||
private void checkCreateFiles(boolean recheck) throws IOException {
|
||||
synchronized(this) {
|
||||
_isChecking = true;
|
||||
try {
|
||||
locked_checkCreateFiles(recheck);
|
||||
} finally {
|
||||
_isChecking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_checkCreateFiles(boolean recheck) throws IOException
|
||||
{
|
||||
// Whether we are resuming or not,
|
||||
// if any of the files already exists we assume we are resuming.
|
||||
@ -867,10 +910,19 @@ public class Storage
|
||||
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||
rafs[nr].seek(0);
|
||||
while (remaining > 0) {
|
||||
int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
|
||||
rafs[nr].write(zeros, 0, size);
|
||||
remaining -= size;
|
||||
// don't bother setting flag for small files
|
||||
if (remaining > 20*1024*1024)
|
||||
_allocateCount.incrementAndGet();
|
||||
try {
|
||||
while (remaining > 0) {
|
||||
int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
|
||||
rafs[nr].write(zeros, 0, size);
|
||||
remaining -= size;
|
||||
}
|
||||
} finally {
|
||||
remaining = lengths[nr];
|
||||
if (remaining > 20*1024*1024)
|
||||
_allocateCount.decrementAndGet();
|
||||
}
|
||||
isSparse[nr] = false;
|
||||
}
|
||||
|
@ -743,6 +743,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
try {
|
||||
// This may take a long time to check the storage, but since it already exists,
|
||||
// it shouldn't be THAT bad, so keep it in this thread.
|
||||
// TODO thread it for big torrents, perhaps a la FetchAndAdd
|
||||
boolean isPrivate = _manager.getPrivateTrackers().contains(announceURL);
|
||||
Storage s = new Storage(_manager.util(), baseFile, announceURL, isPrivate, null);
|
||||
s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
|
||||
@ -992,7 +993,13 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
|
||||
String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
|
||||
String statusString;
|
||||
if (err != null) {
|
||||
if (snark.isChecking()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Checking") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Checking");
|
||||
} else if (snark.isAllocating()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Allocating") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Allocating");
|
||||
} else if (err != null) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
||||
@ -1174,7 +1181,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
String b64 = Base64.encode(snark.getInfoHash());
|
||||
if (showPeers)
|
||||
parameters = parameters + "&p=1";
|
||||
if (isRunning) {
|
||||
if (snark.isChecking()) {
|
||||
// show no buttons
|
||||
} else if (isRunning) {
|
||||
// Stop Button
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
|
Reference in New Issue
Block a user