forked from I2P_Developers/i2p.i2p
i2psnark: Fix deletion of config files, cleanup
orphaned ones at startup (ticket #1498)
This commit is contained in:
@ -27,6 +27,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.app.ClientAppManager;
|
import net.i2p.app.ClientAppManager;
|
||||||
|
import net.i2p.crypto.SHA1Hash;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
@ -537,6 +538,27 @@ public class SnarkManager implements CompleteListener {
|
|||||||
return new File(subdir, hex + CONFIG_FILE_SUFFIX);
|
return new File(subdir, hex + CONFIG_FILE_SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the info hash from a config file name
|
||||||
|
* @return null for invalid name
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
private static SHA1Hash configFileToInfoHash(File file) {
|
||||||
|
String name = file.getName();
|
||||||
|
if (name.length() != 40 + CONFIG_FILE_SUFFIX.length() || !name.endsWith(CONFIG_FILE_SUFFIX))
|
||||||
|
return null;
|
||||||
|
String hex = name.substring(0, 40);
|
||||||
|
byte[] ih = new byte[20];
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
ih[i] = (byte) (Integer.parseInt(hex.substring(i*2, (i*2) + 2), 16) & 0xff);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SHA1Hash(ih);
|
||||||
|
}
|
||||||
|
|
||||||
/** null to set initial defaults */
|
/** null to set initial defaults */
|
||||||
public void loadConfig(String filename) {
|
public void loadConfig(String filename) {
|
||||||
synchronized(_configLock) {
|
synchronized(_configLock) {
|
||||||
@ -1356,6 +1378,7 @@ public class SnarkManager implements CompleteListener {
|
|||||||
snark.stopTorrent();
|
snark.stopTorrent();
|
||||||
_magnets.remove(snark.getName());
|
_magnets.remove(snark.getName());
|
||||||
removeMagnetStatus(snark.getInfoHash());
|
removeMagnetStatus(snark.getInfoHash());
|
||||||
|
removeTorrentStatus(snark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1684,11 +1707,18 @@ public class SnarkManager implements CompleteListener {
|
|||||||
/**
|
/**
|
||||||
* Remove the status of a torrent by removing the config file.
|
* Remove the status of a torrent by removing the config file.
|
||||||
*/
|
*/
|
||||||
public void removeTorrentStatus(MetaInfo metainfo) {
|
private void removeTorrentStatus(Snark snark) {
|
||||||
byte[] ih = metainfo.getInfoHash();
|
byte[] ih = snark.getInfoHash();
|
||||||
File conf = configFile(_configDir, ih);
|
File conf = configFile(_configDir, ih);
|
||||||
synchronized (_configLock) {
|
synchronized (_configLock) {
|
||||||
conf.delete();
|
boolean ok = conf.delete();
|
||||||
|
if (ok) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Deleted " + conf + " for " + snark.getName());
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Failed to delete " + conf + " for " + snark.getName());
|
||||||
|
}
|
||||||
File subdir = conf.getParentFile();
|
File subdir = conf.getParentFile();
|
||||||
String[] files = subdir.list();
|
String[] files = subdir.list();
|
||||||
if (files != null && files.length == 0)
|
if (files != null && files.length == 0)
|
||||||
@ -1696,6 +1726,62 @@ public class SnarkManager implements CompleteListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all orphaned torrent status files, which weren't removed
|
||||||
|
* before 0.9.20, and could be left around after a manual delete also.
|
||||||
|
* Run this once at startup.
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
private void cleanupTorrentStatus() {
|
||||||
|
Set<SHA1Hash> torrents = new HashSet<SHA1Hash>(32);
|
||||||
|
int found = 0;
|
||||||
|
int totalDeleted = 0;
|
||||||
|
synchronized (_snarks) {
|
||||||
|
for (Snark snark : _snarks.values()) {
|
||||||
|
torrents.add(new SHA1Hash(snark.getMetaInfo().getInfoHash()));
|
||||||
|
}
|
||||||
|
synchronized (_configLock) {
|
||||||
|
for (int i = 0; i < B64.length(); i++) {
|
||||||
|
File subdir = new File(_configDir, SUBDIR_PREFIX + B64.charAt(i));
|
||||||
|
File[] configs = subdir.listFiles();
|
||||||
|
if (configs == null)
|
||||||
|
continue;
|
||||||
|
int deleted = 0;
|
||||||
|
for (int j = 0; j < configs.length; j++) {
|
||||||
|
File config = configs[j];
|
||||||
|
SHA1Hash ih = configFileToInfoHash(config);
|
||||||
|
if (ih == null)
|
||||||
|
continue;
|
||||||
|
found++;
|
||||||
|
if (torrents.contains(ih)) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Torrent for " + config + " exists");
|
||||||
|
} else {
|
||||||
|
boolean ok = config.delete();
|
||||||
|
if (ok) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Deleted " + config + " for " + ih);
|
||||||
|
deleted++;
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Failed to delete " + config + " for " + ih);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deleted == configs.length) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Deleting " + subdir);
|
||||||
|
subdir.delete();
|
||||||
|
}
|
||||||
|
totalDeleted += deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Cleanup found " + torrents.size() + " torrents and " + found +
|
||||||
|
" configs, deleted " + totalDeleted + " old configs");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just remember we have it
|
* Just remember we have it
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
@ -1753,7 +1839,8 @@ public class SnarkManager implements CompleteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the torrent, leaving it on the list of torrents unless told to remove it
|
* Stop the torrent, leaving it on the list of torrents unless told to remove it.
|
||||||
|
* If shouldRemove is true, removes the config file also.
|
||||||
*/
|
*/
|
||||||
public Snark stopTorrent(String filename, boolean shouldRemove) {
|
public Snark stopTorrent(String filename, boolean shouldRemove) {
|
||||||
File sfile = new File(filename);
|
File sfile = new File(filename);
|
||||||
@ -1781,6 +1868,8 @@ public class SnarkManager implements CompleteListener {
|
|||||||
// I2PServerSocket.accept() call properly?)
|
// I2PServerSocket.accept() call properly?)
|
||||||
////_util.
|
////_util.
|
||||||
}
|
}
|
||||||
|
if (shouldRemove)
|
||||||
|
removeTorrentStatus(torrent);
|
||||||
if (!wasStopped)
|
if (!wasStopped)
|
||||||
addMessage(_("Torrent stopped: \"{0}\"", torrent.getBaseName()));
|
addMessage(_("Torrent stopped: \"{0}\"", torrent.getBaseName()));
|
||||||
}
|
}
|
||||||
@ -1788,7 +1877,8 @@ public class SnarkManager implements CompleteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the torrent, leaving it on the list of torrents unless told to remove it
|
* Stop the torrent, leaving it on the list of torrents unless told to remove it.
|
||||||
|
* If shouldRemove is true, removes the config file also.
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void stopTorrent(Snark torrent, boolean shouldRemove) {
|
public void stopTorrent(Snark torrent, boolean shouldRemove) {
|
||||||
@ -1801,11 +1891,13 @@ public class SnarkManager implements CompleteListener {
|
|||||||
torrent.stopTorrent();
|
torrent.stopTorrent();
|
||||||
if (!wasStopped)
|
if (!wasStopped)
|
||||||
addMessage(_("Torrent stopped: \"{0}\"", torrent.getBaseName()));
|
addMessage(_("Torrent stopped: \"{0}\"", torrent.getBaseName()));
|
||||||
|
if (shouldRemove)
|
||||||
|
removeTorrentStatus(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the torrent and delete the torrent file itself, but leaving the data
|
* Stop the torrent and delete the torrent file itself, but leaving the data
|
||||||
* behind.
|
* behind. Removes saved config file also.
|
||||||
* Holds the snarks lock to prevent interference from the DirMonitor.
|
* Holds the snarks lock to prevent interference from the DirMonitor.
|
||||||
*/
|
*/
|
||||||
public void removeTorrent(String filename) {
|
public void removeTorrent(String filename) {
|
||||||
@ -1818,9 +1910,6 @@ public class SnarkManager implements CompleteListener {
|
|||||||
File torrentFile = new File(filename);
|
File torrentFile = new File(filename);
|
||||||
torrentFile.delete();
|
torrentFile.delete();
|
||||||
}
|
}
|
||||||
Storage storage = torrent.getStorage();
|
|
||||||
if (storage != null)
|
|
||||||
removeTorrentStatus(storage.getMetaInfo());
|
|
||||||
addMessage(_("Torrent removed: \"{0}\"", torrent.getBaseName()));
|
addMessage(_("Torrent removed: \"{0}\"", torrent.getBaseName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1853,6 +1942,7 @@ public class SnarkManager implements CompleteListener {
|
|||||||
_log.error("Error in the DirectoryMonitor", e);
|
_log.error("Error in the DirectoryMonitor", e);
|
||||||
}
|
}
|
||||||
if (doMagnets) {
|
if (doMagnets) {
|
||||||
|
// first run only
|
||||||
try {
|
try {
|
||||||
addMagnets();
|
addMagnets();
|
||||||
doMagnets = false;
|
doMagnets = false;
|
||||||
@ -1861,6 +1951,9 @@ public class SnarkManager implements CompleteListener {
|
|||||||
}
|
}
|
||||||
if (!_snarks.isEmpty())
|
if (!_snarks.isEmpty())
|
||||||
addMessage(_("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
addMessage(_("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||||
|
// To fix bug where files were left behind,
|
||||||
|
// but also good for when user removes snarks when i2p is not running
|
||||||
|
cleanupTorrentStatus();
|
||||||
}
|
}
|
||||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user