i2psnark: Better handling of read-only i2psnark dir (ticket #1990)

Prevent add/create/remove/delete
More handling of RuntimeException via Snark.fatal() in SnarkManager
This commit is contained in:
zzz
2017-05-12 17:16:25 +00:00
parent 93cb2a0513
commit b340f4a17c
3 changed files with 66 additions and 23 deletions

View File

@ -1243,8 +1243,13 @@ public class Snark
} catch (IOException ioe) { } catch (IOException ioe) {
if (storage != null) { if (storage != null) {
try { storage.close(); } catch (IOException ioee) {} try { storage.close(); } catch (IOException ioee) {}
// clear storage, we have a mess if we have non-null storage and null metainfo,
// as on restart, Storage.reopen() will throw an ioe
storage = null;
} }
fatal("Could not check or create storage", ioe); // TODO we're still in an inconsistent state, won't work if restarted
// (PeerState "disconnecting seed that connects to seeds"
fatal("Could not create data files", ioe);
} }
} }

View File

@ -731,7 +731,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
return new SHA1Hash(ih); return new SHA1Hash(ih);
} }
/** null to set initial defaults */ /** @param filename null to set initial defaults */
public void loadConfig(String filename) { public void loadConfig(String filename) {
synchronized(_configLock) { synchronized(_configLock) {
locked_loadConfig(filename); locked_loadConfig(filename);
@ -896,7 +896,14 @@ public class SnarkManager implements CompleteListener, ClientApp {
_util.setRatingsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_RATINGS, "true"))); _util.setRatingsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_RATINGS, "true")));
_util.setCommentsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_COMMENTS, "true"))); _util.setCommentsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_COMMENTS, "true")));
_util.setCommentsName(_config.getProperty(PROP_COMMENTS_NAME, "")); _util.setCommentsName(_config.getProperty(PROP_COMMENTS_NAME, ""));
getDataDir().mkdirs(); File dd = getDataDir();
if (dd.isDirectory()) {
if (!dd.canWrite())
addMessage(_t("No write permissions for data directory") + ": " + dd);
} else {
if (!dd.mkdirs())
addMessage(_t("Data directory cannot be created") + ": " + dd);
}
initTrackerMap(); initTrackerMap();
} }
@ -1031,6 +1038,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
} else if (!dd.canRead()) { } else if (!dd.canRead()) {
addMessage(_t("Unreadable") + ": " + dataDir); addMessage(_t("Unreadable") + ": " + dataDir);
} else { } else {
if (!dd.canWrite())
addMessage(_t("No write permissions for data directory") + ": " + dataDir);
changed = true; changed = true;
interruptMonitor = true; interruptMonitor = true;
_config.setProperty(PROP_DIR, dataDir); _config.setProperty(PROP_DIR, dataDir);
@ -2640,6 +2649,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
/** /**
* If not connected, thread it, otherwise inline * If not connected, thread it, otherwise inline
* @throws RuntimeException via Snark.fatal()
* @since 0.9.1 * @since 0.9.1
*/ */
public void startTorrent(byte[] infoHash) { public void startTorrent(byte[] infoHash) {
@ -2654,6 +2664,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
/** /**
* If not connected, thread it, otherwise inline * If not connected, thread it, otherwise inline
* @throws RuntimeException via Snark.fatal()
* @since 0.9.23 * @since 0.9.23
*/ */
public void startTorrent(Snark snark) { public void startTorrent(Snark snark) {
@ -2701,17 +2712,14 @@ public class SnarkManager implements CompleteListener, ClientApp {
private final Snark snark; private final Snark snark;
public ThreadedStarter(Snark s) { snark = s; } public ThreadedStarter(Snark s) { snark = s; }
public void run() { public void run() {
try {
run2();
} catch (RuntimeException e) {
_log.error("Error starting", e);
}
}
private void run2() {
if (snark != null) { if (snark != null) {
if (snark.isStopped()) if (snark.isStopped()) {
snark.startTorrent(); try {
snark.startTorrent();
} catch (RuntimeException re) {
// Snark.fatal() will log and call fatal() here for user message before throwing
}
}
} else { } else {
startAll(); startAll();
} }
@ -2724,8 +2732,13 @@ public class SnarkManager implements CompleteListener, ClientApp {
*/ */
private void startAll() { private void startAll() {
for (Snark snark : _snarks.values()) { for (Snark snark : _snarks.values()) {
if (snark.isStopped()) if (snark.isStopped()) {
snark.startTorrent(); try {
snark.startTorrent();
} catch (RuntimeException re) {
// Snark.fatal() will log and call fatal() here for user message before throwing
}
}
} }
} }

View File

@ -987,6 +987,11 @@ public class I2PSnarkServlet extends BasicServlet {
} }
} }
} }
File dd = _manager.getDataDir();
if (!dd.canWrite()) {
_manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
return;
}
if (newURL.startsWith("http://")) { if (newURL.startsWith("http://")) {
FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL, dir); FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL, dir);
_manager.addDownloader(fetch); _manager.addDownloader(fetch);
@ -1045,12 +1050,18 @@ public class I2PSnarkServlet extends BasicServlet {
_manager.addMessage(_t("Magnet deleted: {0}", name)); _manager.addMessage(_t("Magnet deleted: {0}", name));
return; return;
} }
_manager.stopTorrent(snark, true);
// should we delete the torrent file?
// yeah, need to, otherwise it'll get autoadded again (at the moment
File f = new File(name); File f = new File(name);
f.delete(); File dd = _manager.getDataDir();
_manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath())); boolean canDelete = dd.canWrite() || !f.exists();
_manager.stopTorrent(snark, canDelete);
// TODO race here with the DirMonitor, could get re-added
if (f.delete()) {
_manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath()));
} else if (f.exists()) {
if (!canDelete)
_manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
_manager.addMessage(_t("Torrent file could not be deleted: {0}", f.getAbsolutePath()));
}
break; break;
} }
} }
@ -1074,10 +1085,19 @@ public class I2PSnarkServlet extends BasicServlet {
_manager.addMessage(_t("Magnet deleted: {0}", name)); _manager.addMessage(_t("Magnet deleted: {0}", name));
return; return;
} }
_manager.stopTorrent(snark, true);
File f = new File(name); File f = new File(name);
f.delete(); File dd = _manager.getDataDir();
_manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath())); boolean canDelete = dd.canWrite() || !f.exists();
_manager.stopTorrent(snark, canDelete);
// TODO race here with the DirMonitor, could get re-added
if (f.delete()) {
_manager.addMessage(_t("Torrent file deleted: {0}", f.getAbsolutePath()));
} else if (f.exists()) {
if (!canDelete)
_manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
_manager.addMessage(_t("Torrent file could not be deleted: {0}", f.getAbsolutePath()));
return;
}
Storage storage = snark.getStorage(); Storage storage = snark.getStorage();
if (storage == null) if (storage == null)
break; break;
@ -1178,6 +1198,11 @@ public class I2PSnarkServlet extends BasicServlet {
// announceURL = announceURLOther; // announceURL = announceURLOther;
if (baseFile.exists()) { if (baseFile.exists()) {
File dd = _manager.getDataDir();
if (!dd.canWrite()) {
_manager.addMessage(_t("No write permissions for data directory") + ": " + dd);
return;
}
String torrentName = baseFile.getName(); String torrentName = baseFile.getName();
if (torrentName.toLowerCase(Locale.US).endsWith(".torrent")) { if (torrentName.toLowerCase(Locale.US).endsWith(".torrent")) {
_manager.addMessage(_t("Cannot add a torrent ending in \".torrent\": {0}", baseFile.getAbsolutePath())); _manager.addMessage(_t("Cannot add a torrent ending in \".torrent\": {0}", baseFile.getAbsolutePath()));