Prevent part files from being deleted prematurely.
This commit is contained in:
parent
662ba85c5a
commit
e2cb522e87
4 changed files with 75 additions and 18 deletions
|
@ -162,9 +162,8 @@ public class FullBackupImporter extends FullBackupBase {
|
|||
private static void processAttachment(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase db, @NonNull Attachment attachment, BackupRecordInputStream inputStream)
|
||||
throws IOException
|
||||
{
|
||||
File partsDirectory = context.getDir(AttachmentDatabase.DIRECTORY, Context.MODE_PRIVATE);
|
||||
File dataFile = File.createTempFile("part", ".mms", partsDirectory);
|
||||
Pair<byte[], OutputStream> output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false);
|
||||
File dataFile = AttachmentDatabase.newFile(context);
|
||||
Pair<byte[], OutputStream> output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false);
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
|
||||
|
|
|
@ -4,11 +4,8 @@ import android.view.View
|
|||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.Hex
|
||||
import org.thoughtcrime.securesms.util.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||
import java.util.UUID
|
||||
|
||||
object InternalPreference {
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ import java.io.OutputStream;
|
|||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -82,9 +83,10 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AttachmentDatabase extends Database {
|
||||
|
||||
|
||||
private static final String TAG = Log.tag(AttachmentDatabase.class);
|
||||
|
||||
public static final String TABLE_NAME = "part";
|
||||
|
@ -122,7 +124,7 @@ public class AttachmentDatabase extends Database {
|
|||
static final String UPLOAD_TIMESTAMP = "upload_timestamp";
|
||||
static final String CDN_NUMBER = "cdn_number";
|
||||
|
||||
public static final String DIRECTORY = "parts";
|
||||
private static final String DIRECTORY = "parts";
|
||||
|
||||
public static final int TRANSFER_PROGRESS_DONE = 0;
|
||||
public static final int TRANSFER_PROGRESS_STARTED = 1;
|
||||
|
@ -475,14 +477,18 @@ public class AttachmentDatabase extends Database {
|
|||
}
|
||||
|
||||
public int deleteAbandonedAttachmentFiles() {
|
||||
Set<String> filesOnDisk = new HashSet<>();
|
||||
Set<String> filesInDb = new HashSet<>();
|
||||
File[] diskFiles = context.getDir(DIRECTORY, Context.MODE_PRIVATE).listFiles();
|
||||
|
||||
File attachmentDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||
for (File file : attachmentDirectory.listFiles()) {
|
||||
filesOnDisk.add(file.getAbsolutePath());
|
||||
if (diskFiles == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Set<String> filesOnDisk = Arrays.stream(diskFiles)
|
||||
.filter(f -> !PartFileProtector.isProtected(f))
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> filesInDb = new HashSet<>();
|
||||
try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(true, TABLE_NAME, new String[] { DATA }, null, null, null, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
filesInDb.add(CursorUtil.requireString(cursor, DATA));
|
||||
|
@ -866,10 +872,8 @@ public class AttachmentDatabase extends Database {
|
|||
return existing;
|
||||
}
|
||||
|
||||
File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||
File transferFile = File.createTempFile("transfer", ".mms", partsDirectory);
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
File transferFile = newTransferFile();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TRANSFER_FILE, transferFile.getAbsolutePath());
|
||||
|
||||
db.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
|
||||
|
@ -1066,8 +1070,17 @@ public class AttachmentDatabase extends Database {
|
|||
}
|
||||
|
||||
public File newFile() throws IOException {
|
||||
return newFile(context);
|
||||
}
|
||||
|
||||
private File newTransferFile() throws IOException {
|
||||
File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||
return File.createTempFile("part", ".mms", partsDirectory);
|
||||
return PartFileProtector.protect(() -> File.createTempFile("transfer", ".mms", partsDirectory));
|
||||
}
|
||||
|
||||
public static File newFile(Context context) throws IOException {
|
||||
File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||
return PartFileProtector.protect(() -> File.createTempFile("part", ".mms", partsDirectory));
|
||||
}
|
||||
|
||||
private @NonNull DataInfo setAttachmentData(@NonNull File destination,
|
||||
|
@ -1085,6 +1098,7 @@ public class AttachmentDatabase extends Database {
|
|||
|
||||
if (!tempFile.renameTo(destination)) {
|
||||
Log.w(TAG, "Couldn't rename " + tempFile.getPath() + " to " + destination.getPath());
|
||||
tempFile.delete();
|
||||
throw new IllegalStateException("Couldn't rename " + tempFile.getPath() + " to " + destination.getPath());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* In-memory book keeping of newly created part files to prevent them from being
|
||||
* deleted prematurely.
|
||||
*/
|
||||
public final class PartFileProtector {
|
||||
|
||||
private static final long PROTECTED_DURATION = TimeUnit.MINUTES.toMillis(10);
|
||||
|
||||
private static final Map<String, Long> protectedFiles = new HashMap<>();
|
||||
|
||||
public static synchronized File protect(@NonNull CreateFile createFile) throws IOException {
|
||||
File file = createFile.create();
|
||||
protectedFiles.put(file.getAbsolutePath(), System.currentTimeMillis());
|
||||
return file;
|
||||
}
|
||||
|
||||
public static synchronized boolean isProtected(File file) {
|
||||
long timestamp = 0;
|
||||
|
||||
Long protectedTimestamp = protectedFiles.get(file.getAbsolutePath());
|
||||
if (protectedTimestamp != null) {
|
||||
timestamp = Math.max(protectedTimestamp, file.lastModified());
|
||||
}
|
||||
|
||||
boolean isProtected = timestamp > System.currentTimeMillis() - PROTECTED_DURATION;
|
||||
|
||||
if (!isProtected) {
|
||||
protectedFiles.remove(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
return isProtected;
|
||||
}
|
||||
|
||||
interface CreateFile {
|
||||
@NonNull File create() throws IOException;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue