Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1dd7947 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge "LockSettingsStorage: avoid unnecessary directory syncs"

parents 512e5098 03df9123
Loading
Loading
Loading
Loading
+27 −4
Original line number Original line Diff line number Diff line
@@ -309,6 +309,10 @@ class LockSettingsStorage {
    }
    }


    private void writeFile(File path, byte[] data) {
    private void writeFile(File path, byte[] data) {
        writeFile(path, data, /* syncParentDir= */ true);
    }

    private void writeFile(File path, byte[] data, boolean syncParentDir) {
        synchronized (mFileWriteLock) {
        synchronized (mFileWriteLock) {
            // Use AtomicFile to guarantee atomicity of the file write, including when an existing
            // 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,
            // 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);
                file.failWrite(out);
            }
            }
            // For performance reasons, AtomicFile only syncs the file itself, not also the parent
            // 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
            // directory.  The latter must be done explicitly when requested here, as some callers
            // that the file really exists on-disk when this returns.
            // need a guarantee that the file really exists on-disk when this returns.
            if (syncParentDir) {
                fsyncDirectory(path.getParentFile());
                fsyncDirectory(path.getParentFile());
            }
            mCache.putFile(path, data);
            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,
    public void writeSyntheticPasswordState(int userId, long protectorId, String name,
            byte[] data) {
            byte[] data) {
        ensureSyntheticPasswordDirectoryForUser(userId);
        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) {
    public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) {
@@ -392,6 +408,13 @@ class LockSettingsStorage {
        deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
        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) {
    public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) {
        Map<Integer, List<Long>> result = new ArrayMap<>();
        Map<Integer, List<Long>> result = new ArrayMap<>();
        final UserManager um = UserManager.get(mContext);
        final UserManager um = UserManager.get(mContext);
+51 −2
Original line number Original line Diff line number Diff line
@@ -619,12 +619,16 @@ public class SyntheticPasswordManager {


    /**
    /**
     * Creates a new synthetic password (SP) for the given user.
     * Creates a new synthetic password (SP) for the given user.
     *
     * <p>
     * Any existing SID for the user is cleared.
     * Any existing SID for the user is cleared.
     *
     * <p>
     * Also saves the escrow information necessary to re-generate the synthetic password under
     * 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
     * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
     * password escrow should be disabled completely on the given user.
     * 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) {
    SyntheticPassword newSyntheticPassword(int userId) {
        clearSidForUser(userId);
        clearSidForUser(userId);
@@ -668,6 +672,7 @@ public class SyntheticPasswordManager {


    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
        saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId);
        saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId);
        syncState(userId);
    }
    }


    private boolean loadEscrowData(SyntheticPassword sp, int userId) {
    private boolean loadEscrowData(SyntheticPassword sp, int userId) {
@@ -677,6 +682,11 @@ public class SyntheticPasswordManager {
        return e0 != null && p1 != null;
        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) {
    private void saveEscrowData(SyntheticPassword sp, int userId) {
        saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId);
        saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId);
        saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId);
        saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId);
@@ -708,6 +718,10 @@ public class SyntheticPasswordManager {
        return buffer.getInt();
        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) {
    private void saveWeaverSlot(int slot, long protectorId, int userId) {
        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
        buffer.put(WEAVER_VERSION);
        buffer.put(WEAVER_VERSION);
@@ -837,6 +851,7 @@ public class SyntheticPasswordManager {
        }
        }
        createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret,
        createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret,
                sid, userId);
                sid, userId);
        syncState(userId); // ensure the new files are really saved to disk
        return protectorId;
        return protectorId;
    }
    }


@@ -996,6 +1011,7 @@ public class SyntheticPasswordManager {
        saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId);
        saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId);
        createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp,
        createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp,
                tokenData.aggregatedSecret, 0L, userId);
                tokenData.aggregatedSecret, 0L, userId);
        syncState(userId); // ensure the new files are really saved to disk
        tokenMap.get(userId).remove(tokenHandle);
        tokenMap.get(userId).remove(tokenHandle);
        if (tokenData.mCallback != null) {
        if (tokenData.mCallback != null) {
            tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId);
            tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId);
@@ -1003,6 +1019,11 @@ public class SyntheticPasswordManager {
        return true;
        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,
    private void createSyntheticPasswordBlob(long protectorId, byte protectorType,
            SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) {
            SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) {
        final byte[] spSecret;
        final byte[] spSecret;
@@ -1118,6 +1139,7 @@ public class SyntheticPasswordManager {
                            // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
                            // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
                            pwd.credentialType = credential.getType();
                            pwd.credentialType = credential.getType();
                            saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
                            saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
                            syncState(userId);
                            synchronizeFrpPassword(pwd, 0, userId);
                            synchronizeFrpPassword(pwd, 0, userId);
                        } else {
                        } else {
                            Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
                            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() &&
        if (result.syntheticPassword != null && !credential.isNone() &&
                !hasPasswordMetrics(protectorId, userId)) {
                !hasPasswordMetrics(protectorId, userId)) {
            savePasswordMetrics(credential, result.syntheticPassword, 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;
        return result;
    }
    }
@@ -1275,6 +1298,7 @@ public class SyntheticPasswordManager {
                    + blob.mProtectorType);
                    + blob.mProtectorType);
            createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret,
            createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret,
                    sid, userId);
                    sid, userId);
            syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe.
        }
        }
        return result;
        return result;
    }
    }
@@ -1396,12 +1420,21 @@ public class SyntheticPasswordManager {
        return ArrayUtils.concat(data, secdiscardable);
        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) {
    private byte[] createSecdiscardable(long protectorId, int userId) {
        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
        saveSecdiscardable(protectorId, data, userId);
        saveSecdiscardable(protectorId, data, userId);
        return data;
        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) {
    private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) {
        saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId);
        saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId);
    }
    }
@@ -1445,6 +1478,11 @@ public class SyntheticPasswordManager {
        return VersionedPasswordMetrics.deserialize(decrypted).getMetrics();
        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,
    private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp,
            long protectorId, int userId) {
            long protectorId, int userId) {
        final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(),
        final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(),
@@ -1466,10 +1504,21 @@ public class SyntheticPasswordManager {
        return mStorage.readSyntheticPasswordState(userId, protectorId, stateName);
        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) {
    private void saveState(String stateName, byte[] data, long protectorId, int userId) {
        mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data);
        mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data);
    }
    }


    private void syncState(int userId) {
        mStorage.syncSyntheticPasswordState(userId);
    }

    private void destroyState(String stateName, long protectorId, int userId) {
    private void destroyState(String stateName, long protectorId, int userId) {
        mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName);
        mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName);
    }
    }