diff options
| -rw-r--r-- | services/core/java/com/android/server/locksettings/LockSettingsStorage.java | 47 |
1 files changed, 23 insertions, 24 deletions
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index db036b06a77a..d836df56ba1e 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -36,6 +36,7 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.AtomicFile; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -52,6 +53,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; @@ -308,48 +310,45 @@ class LockSettingsStorage { private void writeFile(File path, byte[] data) { synchronized (mFileWriteLock) { - RandomAccessFile raf = null; + // Use AtomicFile to guarantee atomicity of the file write, including when an existing + // file is replaced with a new one. This method is usually used to create new files, + // but there are some edge cases in which it is used to replace an existing file. + AtomicFile file = new AtomicFile(path); + FileOutputStream out = null; try { - // Write the data to the file, requiring each write to be synchronized to the - // underlying storage device immediately to avoid data loss in case of power loss. - raf = new RandomAccessFile(path, "rws"); - // Truncate the file if the data is empty. - if (data == null || data.length == 0) { - raf.setLength(0); - } else { - raf.write(data, 0, data.length); - } - raf.close(); - fsyncDirectory(path.getParentFile()); + out = file.startWrite(); + out.write(data); + file.finishWrite(out); + out = null; } catch (IOException e) { - Slog.e(TAG, "Error writing to file " + e); + Slog.e(TAG, "Error writing file " + path, e); } finally { - if (raf != null) { - try { - raf.close(); - } catch (IOException e) { - Slog.e(TAG, "Error closing file " + e); - } - } + file.failWrite(out); } + // For performance reasons, AtomicFile only syncs the file itself, not also the parent + // directory. The latter must be done explicitly here, as some callers need a guarantee + // that the file really exists on-disk when this returns. + fsyncDirectory(path.getParentFile()); mCache.putFile(path, data); } } private void deleteFile(File path) { synchronized (mFileWriteLock) { + // Zeroize the file to try to make its contents unrecoverable. This is *not* guaranteed + // to be effective, and in fact it usually isn't, but it doesn't hurt. We also don't + // bother zeroizing |path|.new, which may exist from an interrupted AtomicFile write. if (path.exists()) { - // Zeroize the file to try to make its contents unrecoverable. This is *not* - // guaranteed to be effective, and in fact it usually isn't, but it doesn't hurt. try (RandomAccessFile raf = new RandomAccessFile(path, "rws")) { final int fileSize = (int) raf.length(); raf.write(new byte[fileSize]); } catch (Exception e) { Slog.w(TAG, "Failed to zeroize " + path, e); } - path.delete(); - mCache.putFile(path, null); } + // To ensure that |path|.new is deleted if it exists, use AtomicFile.delete() here. + new AtomicFile(path).delete(); + mCache.putFile(path, null); } } |