Chromium Code Reviews| Index: sync/engine/apply_control_data_updates.cc |
| diff --git a/sync/engine/apply_control_data_updates.cc b/sync/engine/apply_control_data_updates.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b144405dbb543f4932e4c12fd527b2ec5e725fc8 |
| --- /dev/null |
| +++ b/sync/engine/apply_control_data_updates.cc |
| @@ -0,0 +1,139 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "sync/engine/apply_control_data_updates.h" |
| + |
| +#include "base/metrics/histogram.h" |
| +#include "sync/engine/conflict_resolver.h" |
| +#include "sync/engine/syncer_util.h" |
| +#include "sync/syncable/directory.h" |
| +#include "sync/syncable/mutable_entry.h" |
| +#include "sync/syncable/nigori_util.h" |
| +#include "sync/syncable/write_transaction.h" |
| +#include "sync/util/cryptographer.h" |
| + |
| +namespace syncer { |
| + |
| +using syncable::GET_BY_SERVER_TAG; |
| +using syncable::IS_UNAPPLIED_UPDATE; |
| +using syncable::IS_UNSYNCED; |
| +using syncable::SERVER_SPECIFICS; |
| +using syncable::SPECIFICS; |
| +using syncable::SYNCER; |
| + |
| +void ApplyControlDataUpdates(syncable::Directory* dir) { |
| + syncable::WriteTransaction trans(FROM_HERE, SYNCER, dir); |
| + |
| + if (ApplyNigoriUpdates(&trans, dir->GetCryptographer(&trans))) { |
| + dir->set_initial_sync_ended_for_type(NIGORI, true); |
| + } |
|
rlarocque
2012/08/11 01:31:52
TODO: Should manually apply PER_DEVICE_METADATA an
|
| +} |
| + |
| +bool ApplyNigoriUpdates(syncable::WriteTransaction* trans, |
| + Cryptographer* cryptographer) { |
| + syncable::MutableEntry nigori_node(trans, GET_BY_SERVER_TAG, |
| + ModelTypeToRootTag(NIGORI)); |
| + |
| + // Mainly for unit tests. We should have a Nigori node by this point. |
| + if (!nigori_node.good()) { |
| + return false; |
| + } |
| + |
| + // We always update the cryptographer with the server's nigori node, |
| + // even if we have a locally modified nigori node (we manually merge nigori |
| + // data in the conflict resolver in that case). This handles the case where |
| + // two clients both set a different passphrase. The second client to attempt |
| + // to commit will go into a state of having pending keys, unioned the set of |
| + // encrypted types, and eventually re-encrypt everything with the passphrase |
| + // of the first client and commit the set of merged encryption keys. Until the |
| + // second client provides the pending passphrase, the cryptographer will |
| + // preserve the encryption keys based on the local passphrase, while the |
| + // nigori node will preserve the server encryption keys. |
| + // |
| + // If non-encryption changes are made to the nigori node, they will be |
| + // lost as part of conflict resolution. This is intended, as we place a higher |
| + // priority on preserving the server's passphrase change to preserving local |
| + // non-encryption changes. Next time the non-encryption changes are made to |
| + // the nigori node (e.g. on restart), they will commit without issue. |
| + if (nigori_node.Get(IS_UNAPPLIED_UPDATE)) { |
| + const sync_pb::NigoriSpecifics& nigori = |
| + nigori_node.Get(SERVER_SPECIFICS).nigori(); |
| + cryptographer->Update(nigori); |
| + |
| + // Make sure any unsynced changes are properly encrypted as necessary. |
| + // We only perform this if the cryptographer is ready. If not, these are |
| + // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything). |
| + // This logic covers the case where the nigori update marked new datatypes |
| + // for encryption, but didn't change the passphrase. |
| + if (cryptographer->is_ready()) { |
| + // Note that we don't bother to encrypt any data for which IS_UNSYNCED |
| + // == false here. The machine that turned on encryption should know about |
| + // and re-encrypt all synced data. It's possible it could get interrupted |
| + // during this process, but we currently reencrypt everything at startup |
| + // as well, so as soon as a client is restarted with this datatype marked |
| + // for encryption, all the data should be updated as necessary. |
| + |
| + // If this fails, something is wrong with the cryptographer, but there's |
| + // nothing we can do about it here. |
| + DVLOG(1) << "Received new nigori, encrypting unsynced changes."; |
| + syncable::ProcessUnsyncedChangesForEncryption(trans, cryptographer); |
| + } |
| + |
| + if (!nigori_node.Get(IS_UNSYNCED)) { // Update only. |
| + UpdateLocalDataFromServerData(trans, &nigori_node); |
| + } else { // Conflict. |
| + // Create a new set of specifics based on the server specifics (which |
| + // preserves their encryption keys). |
| + sync_pb::EntitySpecifics specifics = nigori_node.Get(SERVER_SPECIFICS); |
| + sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori(); |
| + // Store the merged set of encrypted types (cryptographer->Update(..) will |
| + // have merged the local types already). |
| + cryptographer->UpdateNigoriFromEncryptedTypes(server_nigori); |
| + // The cryptographer has the both the local and remote encryption keys |
| + // (added at cryptographer->Update(..) time). |
| + // If the cryptographer is ready, then it already merged both sets of keys |
| + // and we can store them back in. In that case, the remote key was already |
| + // part of the local keybag, so we preserve the local key as the default |
| + // (including whether it's an explicit key). |
| + // If the cryptographer is not ready, then the user will have to provide |
| + // the passphrase to decrypt the pending keys. When they do so, the |
| + // SetDecryptionPassphrase code will act based on whether the server |
| + // update has an explicit passphrase or not. |
| + // - If the server had an explicit passphrase, that explicit passphrase |
| + // will be preserved as the default encryption key. |
| + // - If the server did not have an explicit passphrase, we assume the |
| + // local passphrase is the most up to date and preserve the local |
| + // default encryption key marked as an implicit passphrase. |
| + // This works fine except for the case where we had locally set an |
| + // explicit passphrase. In that case the nigori node will have the default |
| + // key based on the local explicit passphassphrase, but will not have it |
| + // marked as explicit. To fix this we'd have to track whether we have a |
| + // explicit passphrase or not separate from the nigori, which would |
| + // introduce even more complexity, so we leave it up to the user to reset |
| + // that passphrase as an explicit one via settings. The goal here is to |
| + // ensure both sets of encryption keys are preserved. |
| + if (cryptographer->is_ready()) { |
| + cryptographer->GetKeys(server_nigori->mutable_encrypted()); |
| + server_nigori->set_using_explicit_passphrase( |
| + nigori_node.Get(SPECIFICS).nigori(). |
| + using_explicit_passphrase()); |
| + } |
| + // We deliberately leave the server's device information. This client will |
| + // add its own device information on restart. |
| + nigori_node.Put(SPECIFICS, specifics); |
| + DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " |
| + << nigori_node; |
| + |
| + OverwriteServerChanges(&nigori_node); |
| + |
| + UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| + ConflictResolver::NIGORI_MERGE, |
| + ConflictResolver::CONFLICT_RESOLUTION_SIZE); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace syncer |