diff options
author | twyen <twyen@google.com> | 2017-07-19 10:43:51 -0700 |
---|---|---|
committer | Eric Erfanian <erfanian@google.com> | 2017-07-25 16:42:59 +0000 |
commit | 35a248f6351664ab1b3cd7b933efc30521312d39 (patch) | |
tree | 95a6732be9707b5468eb7de10ca59a7f269a31e5 /java | |
parent | 193c724dda95d6ac29b2ab90483080099986977f (diff) |
Handle corrupted persistent log
Previously if the log is corrupted for any reason(interrupted writes, cosmic rays, bad programming skills), the log reader will get an incorrect entry length and try to read a few GB of data.
In this CL a prefix and postfix is added to each log entry. If it doesn't match or if the entry length is dubious, the logs will be purged.
All existing logs will be purged upon sending feedback as a result of the new format.
Bug: 63678731
Test: PersistentLoggerTest
PiperOrigin-RevId: 162504759
Change-Id: I85f39fbc6e9738b9fb0a251131b77d8a15fe44f6
Diffstat (limited to 'java')
-rw-r--r-- | java/com/android/dialer/persistentlog/PersistentLogFileHandler.java | 61 | ||||
-rw-r--r-- | java/com/android/dialer/persistentlog/PersistentLogger.java | 10 |
2 files changed, 69 insertions, 2 deletions
diff --git a/java/com/android/dialer/persistentlog/PersistentLogFileHandler.java b/java/com/android/dialer/persistentlog/PersistentLogFileHandler.java index 5c7a28c5b..8bd8335a2 100644 --- a/java/com/android/dialer/persistentlog/PersistentLogFileHandler.java +++ b/java/com/android/dialer/persistentlog/PersistentLogFileHandler.java @@ -27,6 +27,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.support.v4.os.UserManagerCompat; +import com.android.dialer.common.LogUtil; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -58,6 +59,16 @@ final class PersistentLogFileHandler { private static final String LOG_DIRECTORY = "persistent_log"; private static final String NEXT_FILE_INDEX_PREFIX = "persistent_long_next_file_index_"; + private static final byte[] ENTRY_PREFIX = {'P'}; + private static final byte[] ENTRY_POSTFIX = {'L'}; + + private static class LogCorruptionException extends Exception { + + public LogCorruptionException(String message) { + super(message); + } + }; + private File logDirectory; private final String subfolder; private final int fileSizeLimit; @@ -106,8 +117,10 @@ final class PersistentLogFileHandler { try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(outputFile, true))) { for (byte[] log : logs) { + outputStream.write(ENTRY_PREFIX); outputStream.writeInt(log.length); outputStream.write(log); + outputStream.write(ENTRY_POSTFIX); } outputStream.close(); if (outputFile.length() > fileSizeLimit) { @@ -116,6 +129,21 @@ final class PersistentLogFileHandler { } } + void writeRawLogsForTest(byte[] data) throws IOException { + if (outputFile == null) { + selectNextFileToWrite(); + } + outputFile.createNewFile(); + try (DataOutputStream outputStream = + new DataOutputStream(new FileOutputStream(outputFile, true))) { + outputStream.write(data); + outputStream.close(); + if (outputFile.length() > fileSizeLimit) { + selectNextFileToWrite(); + } + } + } + /** Concatenate all log files in chronicle order and return a byte array. */ @WorkerThread @NonNull @@ -149,10 +177,21 @@ final class PersistentLogFileHandler { logs.add(log); log = readLog(input); } + } catch (LogCorruptionException e) { + LogUtil.e("PersistentLogFileHandler.getLogs", "logs corrupted, deleting", e); + deleteLogs(); + return new ArrayList<>(); } return logs; } + private void deleteLogs() throws IOException { + for (File file : getLogFiles()) { + file.delete(); + } + selectNextFileToWrite(); + } + @WorkerThread private void selectNextFileToWrite() throws IOException { File[] files = getLogFiles(); @@ -186,10 +225,28 @@ final class PersistentLogFileHandler { @Nullable @WorkerThread - private static byte[] readLog(DataInputStream inputStream) throws IOException { + private byte[] readLog(DataInputStream inputStream) throws IOException, LogCorruptionException { try { - byte[] data = new byte[inputStream.readInt()]; + byte[] prefix = new byte[ENTRY_PREFIX.length]; + if (inputStream.read(prefix) == -1) { + // EOF + return null; + } + if (!Arrays.equals(prefix, ENTRY_PREFIX)) { + throw new LogCorruptionException("entry prefix mismatch"); + } + int dataLength = inputStream.readInt(); + if (dataLength > fileSizeLimit) { + throw new LogCorruptionException("data length over max size"); + } + byte[] data = new byte[dataLength]; inputStream.read(data); + + byte[] postfix = new byte[ENTRY_POSTFIX.length]; + inputStream.read(postfix); + if (!Arrays.equals(postfix, ENTRY_POSTFIX)) { + throw new LogCorruptionException("entry postfix mismatch"); + } return data; } catch (EOFException e) { return null; diff --git a/java/com/android/dialer/persistentlog/PersistentLogger.java b/java/com/android/dialer/persistentlog/PersistentLogger.java index 7d82ec1a1..847625ec5 100644 --- a/java/com/android/dialer/persistentlog/PersistentLogger.java +++ b/java/com/android/dialer/persistentlog/PersistentLogger.java @@ -110,6 +110,16 @@ public final class PersistentLogger { loggerThreadHandler.sendEmptyMessageDelayed(MESSAGE_FLUSH, FLUSH_DELAY_MILLIS); } + @VisibleForTesting + /** write raw bytes directly to the log file, likely corrupting it. */ + static void rawLogForTest(byte[] data) { + try { + fileHandler.writeRawLogsForTest(data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** Dump the log as human readable string. Blocks until the dump is finished. */ @NonNull @WorkerThread |