Merge "LockSettingsStorage: avoid unnecessary directory syncs"
This commit is contained in:
@@ -309,6 +309,10 @@ class LockSettingsStorage {
|
||||
}
|
||||
|
||||
private void writeFile(File path, byte[] data) {
|
||||
writeFile(path, data, /* syncParentDir= */ true);
|
||||
}
|
||||
|
||||
private void writeFile(File path, byte[] data, boolean syncParentDir) {
|
||||
synchronized (mFileWriteLock) {
|
||||
// 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,
|
||||
@@ -326,9 +330,11 @@ class LockSettingsStorage {
|
||||
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());
|
||||
// directory. The latter must be done explicitly when requested here, as some callers
|
||||
// need a guarantee that the file really exists on-disk when this returns.
|
||||
if (syncParentDir) {
|
||||
fsyncDirectory(path.getParentFile());
|
||||
}
|
||||
mCache.putFile(path, data);
|
||||
}
|
||||
}
|
||||
@@ -378,10 +384,20 @@ class LockSettingsStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the synthetic password state file for the given user ID, protector ID, and state name.
|
||||
* If the file already exists, then it is atomically replaced.
|
||||
* <p>
|
||||
* This doesn't sync the parent directory, and a result the new state file may be lost if the
|
||||
* system crashes. The caller must call {@link syncSyntheticPasswordState()} afterwards to sync
|
||||
* the parent directory if needed, preferably after batching up other state file creations for
|
||||
* the same user. We do it this way because directory syncs are expensive on some filesystems.
|
||||
*/
|
||||
public void writeSyntheticPasswordState(int userId, long protectorId, String name,
|
||||
byte[] data) {
|
||||
ensureSyntheticPasswordDirectoryForUser(userId);
|
||||
writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data);
|
||||
writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data,
|
||||
/* syncParentDir= */ false);
|
||||
}
|
||||
|
||||
public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) {
|
||||
@@ -392,6 +408,13 @@ class LockSettingsStorage {
|
||||
deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all synthetic password state files for the user have really been saved to disk.
|
||||
*/
|
||||
public void syncSyntheticPasswordState(int userId) {
|
||||
fsyncDirectory(getSyntheticPasswordDirectoryForUser(userId));
|
||||
}
|
||||
|
||||
public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) {
|
||||
Map<Integer, List<Long>> result = new ArrayMap<>();
|
||||
final UserManager um = UserManager.get(mContext);
|
||||
|
||||
@@ -619,12 +619,16 @@ public class SyntheticPasswordManager {
|
||||
|
||||
/**
|
||||
* Creates a new synthetic password (SP) for the given user.
|
||||
*
|
||||
* <p>
|
||||
* Any existing SID for the user is cleared.
|
||||
*
|
||||
* <p>
|
||||
* Also saves the escrow information necessary to re-generate the synthetic password under
|
||||
* an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
|
||||
* password escrow should be disabled completely on the given user.
|
||||
* <p>
|
||||
* {@link syncState()} is not called yet; the caller should create a protector afterwards, which
|
||||
* handles this. This makes it so that all the user's initial SP state files, including the
|
||||
* initial LSKF-based protector, are efficiently created with only a single {@link syncState()}.
|
||||
*/
|
||||
SyntheticPassword newSyntheticPassword(int userId) {
|
||||
clearSidForUser(userId);
|
||||
@@ -668,6 +672,7 @@ public class SyntheticPasswordManager {
|
||||
|
||||
private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
|
||||
saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId);
|
||||
syncState(userId);
|
||||
}
|
||||
|
||||
private boolean loadEscrowData(SyntheticPassword sp, int userId) {
|
||||
@@ -677,6 +682,11 @@ public class SyntheticPasswordManager {
|
||||
return e0 != null && p1 != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the escrow data for the synthetic password. The caller is responsible for calling
|
||||
* {@link syncState()} afterwards, once the user's other initial synthetic password state files
|
||||
* have been created.
|
||||
*/
|
||||
private void saveEscrowData(SyntheticPassword sp, int userId) {
|
||||
saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId);
|
||||
saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId);
|
||||
@@ -708,6 +718,10 @@ public class SyntheticPasswordManager {
|
||||
return buffer.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file that stores the Weaver slot the protector is using. The caller is responsible
|
||||
* for calling {@link syncState()} afterwards, once all the protector's files have been created.
|
||||
*/
|
||||
private void saveWeaverSlot(int slot, long protectorId, int userId) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
|
||||
buffer.put(WEAVER_VERSION);
|
||||
@@ -837,6 +851,7 @@ public class SyntheticPasswordManager {
|
||||
}
|
||||
createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret,
|
||||
sid, userId);
|
||||
syncState(userId); // ensure the new files are really saved to disk
|
||||
return protectorId;
|
||||
}
|
||||
|
||||
@@ -996,6 +1011,7 @@ public class SyntheticPasswordManager {
|
||||
saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId);
|
||||
createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp,
|
||||
tokenData.aggregatedSecret, 0L, userId);
|
||||
syncState(userId); // ensure the new files are really saved to disk
|
||||
tokenMap.get(userId).remove(tokenHandle);
|
||||
if (tokenData.mCallback != null) {
|
||||
tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId);
|
||||
@@ -1003,6 +1019,11 @@ public class SyntheticPasswordManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a synthetic password blob, i.e. the file that stores the encrypted synthetic password
|
||||
* (or encrypted escrow secret) for a protector. The caller is responsible for calling
|
||||
* {@link syncState()} afterwards, once all the protector's files have been created.
|
||||
*/
|
||||
private void createSyntheticPasswordBlob(long protectorId, byte protectorType,
|
||||
SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) {
|
||||
final byte[] spSecret;
|
||||
@@ -1118,6 +1139,7 @@ public class SyntheticPasswordManager {
|
||||
// (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
|
||||
pwd.credentialType = credential.getType();
|
||||
saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
|
||||
syncState(userId);
|
||||
synchronizeFrpPassword(pwd, 0, userId);
|
||||
} else {
|
||||
Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
|
||||
@@ -1156,6 +1178,7 @@ public class SyntheticPasswordManager {
|
||||
if (result.syntheticPassword != null && !credential.isNone() &&
|
||||
!hasPasswordMetrics(protectorId, userId)) {
|
||||
savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId);
|
||||
syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1275,6 +1298,7 @@ public class SyntheticPasswordManager {
|
||||
+ blob.mProtectorType);
|
||||
createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret,
|
||||
sid, userId);
|
||||
syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1396,12 +1420,21 @@ public class SyntheticPasswordManager {
|
||||
return ArrayUtils.concat(data, secdiscardable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and writes the secdiscardable file for the given protector. The caller is
|
||||
* responsible for calling {@link syncState()} afterwards, once all the protector's files have
|
||||
* been created.
|
||||
*/
|
||||
private byte[] createSecdiscardable(long protectorId, int userId) {
|
||||
byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
|
||||
saveSecdiscardable(protectorId, data, userId);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the secdiscardable file for the given protector. The caller is responsible for
|
||||
* calling {@link syncState()} afterwards, once all the protector's files have been created.
|
||||
*/
|
||||
private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) {
|
||||
saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId);
|
||||
}
|
||||
@@ -1445,6 +1478,11 @@ public class SyntheticPasswordManager {
|
||||
return VersionedPasswordMetrics.deserialize(decrypted).getMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the password metrics file: the file associated with the LSKF-based protector that
|
||||
* contains the encrypted metrics about the LSKF. The caller is responsible for calling
|
||||
* {@link syncState()} afterwards if needed.
|
||||
*/
|
||||
private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp,
|
||||
long protectorId, int userId) {
|
||||
final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(),
|
||||
@@ -1466,10 +1504,21 @@ public class SyntheticPasswordManager {
|
||||
return mStorage.readSyntheticPasswordState(userId, protectorId, stateName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the given synthetic password state for the given user ID and protector ID.
|
||||
* <p>
|
||||
* For performance reasons, this doesn't sync the user's synthetic password state directory. As
|
||||
* a result, it doesn't guarantee that the file will really be present after a crash. If that
|
||||
* is needed, call {@link syncState()} afterwards, preferably after batching up related updates.
|
||||
*/
|
||||
private void saveState(String stateName, byte[] data, long protectorId, int userId) {
|
||||
mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data);
|
||||
}
|
||||
|
||||
private void syncState(int userId) {
|
||||
mStorage.syncSyntheticPasswordState(userId);
|
||||
}
|
||||
|
||||
private void destroyState(String stateName, long protectorId, int userId) {
|
||||
mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user