Fix #85: Remove downloaded index files that hang around.

If the download process is interrupted, a "dl-" file hangs around in
F-Droid's cache directory. If the download succeeds, extracts an xml
file from the downloaded .jar file, and then the rest of the process
is interrupted, then a "index-*.xml" file hangs around in F-Droids
files directory.

This fix removes these two types of left over files when F-Droid
is started. In addition, it also makes it so that the downloaded
repo indexes are now more clearly named "index-*-downloaded" (with no
extension, because the code which does it doesn't know whether it is
a .jar or .xml file, and it doesn't matter anyway). Also, it extracts
the .xml files to the cache directory too (instead of the files dir)
and names them "index-*-extracted.xml".

There is some code which removes the old "dl-" files that will become
redundant after the first time it is executed on a users device after
upgrading F-Droid. It is a very small amount of code run on startup,
so I am not concerned about the performance implications of leaving it
there, but I put a comment explaining that it can be removed in the
future.

Includes minor formatting changes, and a few annotations.
This commit is contained in:
Peter Serwylo 2015-03-19 07:31:05 +11:00
parent c2eb9a9b67
commit eaea0e2c23
5 changed files with 68 additions and 41 deletions

View File

@ -166,21 +166,24 @@ public class FDroidApp extends Application {
.getDefaultSharedPreferences(getBaseContext());
curTheme = Theme.valueOf(prefs.getString(Preferences.PREF_THEME, "dark"));
if (!prefs.getBoolean(Preferences.PREF_CACHE_APK, false)) {
Utils.deleteFiles(Utils.getApkCacheDir(this), null, ".apk");
}
File local_path = Utils.getApkCacheDir(this);
// Things can be null if the SD card is not ready - we'll just
// ignore that and do it next time.
if (local_path != null) {
final File[] files = local_path.listFiles();
if (files != null) {
for (File f : files) {
if (f.getName().endsWith(".apk")) {
f.delete();
}
}
}
}
}
// Index files which downloaded, but were not removed (e.g. due to F-Droid being force
// closed during processing of the file, before getting a chance to delete). This may
// include both "index-*-downloaded" and "index-*-extracted.xml" files. The first is from
// either signed or unsigned repos, and the later is from signed repos.
Utils.deleteFiles(getCacheDir(), "index-", null);
// As above, but for legacy F-Droid clients that downloaded under a different name, and
// extracted to the files directory rather than the cache directory.
// TODO: This can be removed in a a few months or a year (e.g. 2016) because people will
// have upgraded their clients, this code will have executed, and they will not have any
// left over files any more. Even if they do hold off upgrading until this code is removed,
// the only side effect is that they will have a few more MiB of storage taken up on their
// device until they uninstall and re-install F-Droid.
Utils.deleteFiles(getCacheDir(), "dl-", null);
Utils.deleteFiles(getFilesDir(), "index-", null);
UpdateService.schedule(getApplicationContext());
bluetoothAdapter = getBluetoothAdapter();

View File

@ -23,6 +23,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.Html;
import android.text.TextUtils;
@ -45,7 +47,6 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
@ -72,6 +73,8 @@ public final class Utils {
public static final SimpleDateFormat LOG_DATE_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
private static final String TAG = "org.fdroid.fdroid.Utils";
public static String getIconsDir(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
String iconsDir;
@ -511,4 +514,39 @@ public final class Utils {
return false;
}
/**
* Remove all files from the {@parm directory} either beginning with {@param startsWith}
* or ending with {@param endsWith}. Note that if the SD card is not ready, then the
* cache directory will probably not be available. In this situation no files will be
* deleted (and thus they may still exist after the SD card becomes available).
*/
public static void deleteFiles(@Nullable File directory, @Nullable String startsWith, @Nullable String endsWith) {
if (directory == null) {
return;
}
final File[] files = directory.listFiles();
if (files == null) {
return;
}
if (startsWith != null) {
Log.i(TAG, "Cleaning up files in " + directory + " that start with \"" + startsWith + "\"");
}
if (endsWith != null) {
Log.i(TAG, "Cleaning up files in " + directory + " that end with \"" + endsWith + "\"");
}
for (File f : files) {
if ((startsWith != null && f.getName().startsWith(startsWith))
|| (endsWith != null && f.getName().endsWith(endsWith))) {
if (!f.delete()) {
Log.i(TAG, "Couldn't delete cache file " + f);
}
}
}
}
}

View File

@ -2,6 +2,7 @@ package org.fdroid.fdroid.net;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import org.fdroid.fdroid.ProgressListener;
@ -33,13 +34,13 @@ public abstract class Downloader {
public abstract InputStream getInputStream() throws IOException;
// The context is required for opening the file to write to.
Downloader(String destFile, Context ctx)
Downloader(String destFile, @NonNull Context ctx)
throws FileNotFoundException, MalformedURLException {
this(new File(ctx.getFilesDir() + File.separator + destFile));
}
// The context is required for opening the file to write to.
Downloader(Context ctx) throws IOException {
Downloader(@NonNull Context ctx) throws IOException {
this(File.createTempFile("dl-", "", ctx.getCacheDir()));
}

View File

@ -82,15 +82,15 @@ abstract public class RepoUpdater {
*
* @throws UpdateException All error states will come from here.
*/
protected abstract File getIndexFromFile(File downloadedFile) throws
UpdateException;
protected abstract File getIndexFromFile(File downloadedFile) throws UpdateException;
protected abstract String getIndexAddress();
protected Downloader downloadIndex() throws UpdateException {
Downloader downloader = null;
try {
downloader = DownloaderFactory.create(getIndexAddress(), context);
downloader = DownloaderFactory.create(
getIndexAddress(), File.createTempFile("index-", "-downloaded", context.getCacheDir()));
downloader.setCacheTag(repo.lastetag);
if (progressListener != null) { // interactive session, show progress
@ -104,18 +104,14 @@ abstract public class RepoUpdater {
if (downloader.isCached()) {
// The index is unchanged since we last read it. We just mark
// everything that came from this repo as being updated.
Log.d("FDroid", "Repo index for " + getIndexAddress()
+ " is up to date (by etag)");
Log.d("FDroid", "Repo index for " + getIndexAddress() + " is up to date (by etag)");
}
} catch (IOException e) {
if (downloader != null && downloader.getFile() != null) {
downloader.getFile().delete();
}
throw new UpdateException(
repo,
"Error getting index file from " + repo.address,
e);
throw new UpdateException(repo, "Error getting index file from " + repo.address, e);
}
return downloader;
}
@ -177,21 +173,10 @@ abstract public class RepoUpdater {
rememberer.repo = repo;
rememberer.values = prepareRepoDetailsForSaving(handler, downloader.getCacheTag());
}
} catch (SAXException e) {
throw new UpdateException(
repo, "Error parsing index for repo " + repo.address, e);
} catch (FileNotFoundException e) {
throw new UpdateException(
repo, "Error parsing index for repo " + repo.address, e);
} catch (ParserConfigurationException e) {
throw new UpdateException(
repo, "Error parsing index for repo " + repo.address, e);
} catch (IOException e) {
throw new UpdateException(
repo, "Error parsing index for repo " + repo.address, e);
} catch (SAXException | ParserConfigurationException | IOException e) {
throw new UpdateException(repo, "Error parsing index for repo " + repo.address, e);
} finally {
if (downloadedFile != null &&
downloadedFile != indexFile && downloadedFile.exists()) {
if (downloadedFile != null && downloadedFile != indexFile && downloadedFile.exists()) {
downloadedFile.delete();
}
if (indexFile != null && indexFile.exists()) {

View File

@ -58,7 +58,7 @@ public class SignedRepoUpdater extends RepoUpdater {
jarFile = new JarFile(indexJar, true);
JarEntry indexEntry = (JarEntry)jarFile.getEntry("index.xml");
indexFile = File.createTempFile("index-", ".xml", context.getFilesDir());
indexFile = File.createTempFile("index-", "-extracted.xml", context.getCacheDir());
InputStream input = null;
OutputStream output = null;
try {