Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1196)

Side by Side Diff: sync/internal_api/write_node.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/internal_api/user_share.cc ('k') | sync/internal_api/write_transaction.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "sync/internal_api/public/write_node.h"
6
7 #include <stdint.h>
8
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "sync/internal_api/public/base_transaction.h"
13 #include "sync/internal_api/public/write_transaction.h"
14 #include "sync/internal_api/syncapi_internal.h"
15 #include "sync/protocol/bookmark_specifics.pb.h"
16 #include "sync/protocol/typed_url_specifics.pb.h"
17 #include "sync/syncable/mutable_entry.h"
18 #include "sync/syncable/nigori_util.h"
19 #include "sync/syncable/syncable_util.h"
20 #include "sync/util/cryptographer.h"
21
22 using std::string;
23 using std::vector;
24
25 namespace syncer {
26
27 using syncable::kEncryptedString;
28 using syncable::SPECIFICS;
29
30 static const char kDefaultNameForNewNodes[] = " ";
31
32 void WriteNode::SetIsFolder(bool folder) {
33 if (entry_->GetIsDir() == folder)
34 return; // Skip redundant changes.
35
36 entry_->PutIsDir(folder);
37 MarkForSyncing();
38 }
39
40 void WriteNode::SetTitle(const std::string& title) {
41 DCHECK_NE(GetModelType(), UNSPECIFIED);
42 ModelType type = GetModelType();
43 // It's possible the nigori lost the set of encrypted types. If the current
44 // specifics are already encrypted, we want to ensure we continue encrypting.
45 bool needs_encryption = GetTransaction()->GetEncryptedTypes().Has(type) ||
46 entry_->GetSpecifics().has_encrypted();
47
48 // If this datatype is encrypted and is not a bookmark, we disregard the
49 // specified title in favor of kEncryptedString. For encrypted bookmarks the
50 // NON_UNIQUE_NAME will still be kEncryptedString, but we store the real title
51 // into the specifics. All strings compared are server legal strings.
52 std::string new_legal_title;
53 if (type != BOOKMARKS && needs_encryption) {
54 new_legal_title = kEncryptedString;
55 } else {
56 DCHECK(base::IsStringUTF8(title));
57 SyncAPINameToServerName(title, &new_legal_title);
58 base::TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
59 }
60
61 std::string current_legal_title;
62 if (BOOKMARKS == type &&
63 entry_->GetSpecifics().has_encrypted()) {
64 // Encrypted bookmarks only have their title in the unencrypted specifics.
65 current_legal_title = GetBookmarkSpecifics().title();
66 } else {
67 // Non-bookmarks and legacy bookmarks (those with no title in their
68 // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
69 // store their title in specifics as well as NON_UNIQUE_NAME.
70 current_legal_title = entry_->GetNonUniqueName();
71 }
72
73 bool title_matches = (current_legal_title == new_legal_title);
74 bool encrypted_without_overwriting_name = (needs_encryption &&
75 entry_->GetNonUniqueName() != kEncryptedString);
76
77 // For bookmarks, we also set the title field in the specifics.
78 // TODO(zea): refactor bookmarks to not need this functionality.
79 sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
80 if (GetModelType() == BOOKMARKS &&
81 specifics.bookmark().title() != new_legal_title) {
82 specifics.mutable_bookmark()->set_title(new_legal_title);
83 SetEntitySpecifics(specifics); // Does it's own encryption checking.
84 title_matches = false;
85 }
86
87 // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
88 // necessary, nothing needs to change.
89 if (title_matches && !encrypted_without_overwriting_name) {
90 DVLOG(2) << "Title matches, dropping change.";
91 return;
92 }
93
94 // For bookmarks, this has to happen after we set the title in the specifics,
95 // because the presence of a title in the NON_UNIQUE_NAME is what controls
96 // the logic deciding whether this is an empty node or a legacy bookmark.
97 // See BaseNode::GetUnencryptedSpecific(..).
98 if (needs_encryption)
99 entry_->PutNonUniqueName(kEncryptedString);
100 else
101 entry_->PutNonUniqueName(new_legal_title);
102
103 DVLOG(1) << "Overwriting title of type "
104 << ModelTypeToString(type)
105 << " and marking for syncing.";
106 MarkForSyncing();
107 }
108
109 void WriteNode::SetBookmarkSpecifics(
110 const sync_pb::BookmarkSpecifics& new_value) {
111 sync_pb::EntitySpecifics entity_specifics;
112 entity_specifics.mutable_bookmark()->CopyFrom(new_value);
113 SetEntitySpecifics(entity_specifics);
114 }
115
116 void WriteNode::SetNigoriSpecifics(
117 const sync_pb::NigoriSpecifics& new_value) {
118 sync_pb::EntitySpecifics entity_specifics;
119 entity_specifics.mutable_nigori()->CopyFrom(new_value);
120 SetEntitySpecifics(entity_specifics);
121 }
122
123 void WriteNode::SetPasswordSpecifics(
124 const sync_pb::PasswordSpecificsData& data) {
125 DCHECK_EQ(GetModelType(), PASSWORDS);
126
127 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
128
129 // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
130 // because Passwords have their encrypted data within the PasswordSpecifics,
131 // vs within the EntitySpecifics like all the other types.
132 const sync_pb::EntitySpecifics& old_specifics = GetEntitySpecifics();
133 sync_pb::EntitySpecifics entity_specifics;
134 // Copy over the old specifics if they exist.
135 if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
136 entity_specifics.CopyFrom(old_specifics);
137 } else {
138 AddDefaultFieldValue(PASSWORDS, &entity_specifics);
139 }
140 sync_pb::PasswordSpecifics* password_specifics =
141 entity_specifics.mutable_password();
142 // This will only update password_specifics if the underlying unencrypted blob
143 // was different from |data| or was not encrypted with the proper passphrase.
144 if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
145 LOG(ERROR) << "Failed to encrypt password, possibly due to sync node "
146 << "corruption";
147 return;
148 }
149 SetEntitySpecifics(entity_specifics);
150 }
151
152 void WriteNode::SetEntitySpecifics(
153 const sync_pb::EntitySpecifics& new_value) {
154 ModelType new_specifics_type =
155 GetModelTypeFromSpecifics(new_value);
156 CHECK(!new_value.password().has_client_only_encrypted_data());
157 DCHECK_NE(new_specifics_type, UNSPECIFIED);
158 DVLOG(1) << "Writing entity specifics of type "
159 << ModelTypeToString(new_specifics_type);
160 DCHECK_EQ(new_specifics_type, GetModelType());
161
162 // Preserve unknown fields.
163 const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
164 sync_pb::EntitySpecifics new_specifics;
165 new_specifics.CopyFrom(new_value);
166 new_specifics.mutable_unknown_fields()
167 ->append(old_specifics.unknown_fields());
168
169 // Will update the entry if encryption was necessary.
170 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
171 new_specifics,
172 entry_)) {
173 return;
174 }
175 if (entry_->GetSpecifics().has_encrypted()) {
176 // EncryptIfNecessary already updated the entry for us and marked for
177 // syncing if it was needed. Now we just make a copy of the unencrypted
178 // specifics so that if this node is updated, we do not have to decrypt the
179 // old data. Note that this only modifies the node's local data, not the
180 // entry itself.
181 SetUnencryptedSpecifics(new_value);
182 }
183
184 DCHECK_EQ(new_specifics_type, GetModelType());
185 }
186
187 void WriteNode::ResetFromSpecifics() {
188 SetEntitySpecifics(GetEntitySpecifics());
189 }
190
191 void WriteNode::SetTypedUrlSpecifics(
192 const sync_pb::TypedUrlSpecifics& new_value) {
193 sync_pb::EntitySpecifics entity_specifics;
194 entity_specifics.mutable_typed_url()->CopyFrom(new_value);
195 SetEntitySpecifics(entity_specifics);
196 }
197
198 void WriteNode::SetExternalId(int64_t id) {
199 if (GetExternalId() != id)
200 entry_->PutLocalExternalId(id);
201 }
202
203 WriteNode::WriteNode(WriteTransaction* transaction)
204 : entry_(NULL), transaction_(transaction) {
205 DCHECK(transaction);
206 }
207
208 WriteNode::~WriteNode() {
209 delete entry_;
210 }
211
212 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
213 // Return true on success.
214 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64_t id) {
215 DCHECK(!entry_) << "Init called twice";
216 DCHECK_NE(id, kInvalidId);
217 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
218 syncable::GET_BY_HANDLE, id);
219 if (!entry_->good())
220 return INIT_FAILED_ENTRY_NOT_GOOD;
221 if (entry_->GetIsDel())
222 return INIT_FAILED_ENTRY_IS_DEL;
223 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
224 }
225
226 // Find a node by client tag, and bind this WriteNode to it.
227 // Return true if the write node was found, and was not deleted.
228 // Undeleting a deleted node is possible by ClientTag.
229 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
230 ModelType model_type,
231 const std::string& tag) {
232 DCHECK(!entry_) << "Init called twice";
233 if (tag.empty())
234 return INIT_FAILED_PRECONDITION;
235
236 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
237
238 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
239 syncable::GET_BY_CLIENT_TAG, hash);
240 if (!entry_->good())
241 return INIT_FAILED_ENTRY_NOT_GOOD;
242 if (entry_->GetIsDel())
243 return INIT_FAILED_ENTRY_IS_DEL;
244 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
245 }
246
247 BaseNode::InitByLookupResult WriteNode::InitTypeRoot(ModelType type) {
248 DCHECK(!entry_) << "Init called twice";
249 if (!IsRealDataType(type))
250 return INIT_FAILED_PRECONDITION;
251 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
252 syncable::GET_TYPE_ROOT, type);
253 if (!entry_->good())
254 return INIT_FAILED_ENTRY_NOT_GOOD;
255 if (entry_->GetIsDel())
256 return INIT_FAILED_ENTRY_IS_DEL;
257 ModelType model_type = GetModelType();
258 DCHECK_EQ(model_type, NIGORI);
259 return INIT_OK;
260 }
261
262 // Create a new node with default properties, and bind this WriteNode to it.
263 // Return true on success.
264 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
265 const BaseNode* predecessor) {
266 DCHECK(!entry_) << "Init called twice";
267 // |predecessor| must be a child of |parent| or NULL.
268 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
269 DCHECK(false);
270 return false;
271 }
272
273 syncable::Id parent_id = parent.GetSyncId();
274 DCHECK(!parent_id.IsNull());
275
276 // Start out with a dummy name. We expect
277 // the caller to set a meaningful name after creation.
278 string dummy(kDefaultNameForNewNodes);
279
280 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
281 syncable::CREATE, BOOKMARKS,
282 parent_id, dummy);
283
284 if (!entry_->good())
285 return false;
286
287 // Entries are untitled folders by default.
288 entry_->PutIsDir(true);
289
290 if (!PutPredecessor(predecessor)) {
291 return false;
292 }
293
294 // Mark this entry as unsynced, to wake up the syncer.
295 MarkForSyncing();
296 return true;
297 }
298
299 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
300 ModelType model_type,
301 const BaseNode& parent,
302 const std::string& tag) {
303 return InitUniqueByCreationImpl(model_type, parent.GetSyncId(), tag);
304 }
305
306 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
307 ModelType model_type,
308 const std::string& tag) {
309 return InitUniqueByCreationImpl(model_type, syncable::Id(), tag);
310 }
311
312 // Create a new node with default properties and a client defined unique tag,
313 // and bind this WriteNode to it.
314 // Return true on success. If the tag exists in the database, then
315 // we will attempt to undelete the node.
316 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreationImpl(
317 ModelType model_type,
318 const syncable::Id& parent_id,
319 const std::string& tag) {
320 // This DCHECK will only fail if init is called twice.
321 DCHECK(!entry_);
322 if (tag.empty()) {
323 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
324 return INIT_FAILED_EMPTY_TAG;
325 }
326
327 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
328
329 // Start out with a dummy name. We expect
330 // the caller to set a meaningful name after creation.
331 string dummy(kDefaultNameForNewNodes);
332
333 // Check if we have this locally and need to undelete it.
334 std::unique_ptr<syncable::MutableEntry> existing_entry(
335 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
336 syncable::GET_BY_CLIENT_TAG, hash));
337
338 if (existing_entry->good()) {
339 bool entry_undeleted = false;
340 if (existing_entry->GetIsDel()) {
341 // Rules for undelete:
342 // BASE_VERSION: Must keep the same.
343 // ID: Essential to keep the same.
344 // META_HANDLE: Must be the same, so we can't "split" the entry.
345 // IS_DEL: Must be set to false, will cause reindexing.
346 // This one is weird because IS_DEL is true for "update only"
347 // items. It should be OK to undelete an update only.
348 // MTIME/CTIME: Seems reasonable to just leave them alone.
349 // IS_UNSYNCED: Must set this to true or face database insurrection.
350 // We do this below this block.
351 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
352 // to SERVER_VERSION. We keep it the same here.
353 // IS_DIR: We'll leave it the same.
354 // SPECIFICS: Reset it.
355
356 // Put specifics to define the entry's model type to handle the case
357 // where this is not actually an undeletion, but instead a collision
358 // with a newly downloaded, processed, and unapplied server update.
359 // This should be done first before inserting the entry into the
360 // directory's ParentChildIndex by clearing its "deleted" flag below.
361 // This is a fix for http://crbug.com/505761.
362 sync_pb::EntitySpecifics specifics;
363 AddDefaultFieldValue(model_type, &specifics);
364 existing_entry->PutSpecifics(specifics);
365
366 existing_entry->PutIsDel(false);
367
368 // Client tags are immutable and must be paired with the ID.
369 // If a server update comes down with an ID and client tag combo,
370 // and it already exists, always overwrite it and store only one copy.
371 // We have to undelete entries because we can't disassociate IDs from
372 // tags and updates.
373
374 existing_entry->PutNonUniqueName(dummy);
375 existing_entry->PutParentId(parent_id);
376 entry_undeleted = true;
377 } // Else just reuse the existing entry.
378 entry_ = existing_entry.release();
379 // If entry is undeleted, its specifics are reset to default, unencrypted
380 // value, and therefore no decryption is necessary. Moreover trying to
381 // decrypt the password entry will fail because passwords are expected to be
382 // encrypted.
383 if (!entry_undeleted && !DecryptIfNecessary())
384 return INIT_FAILED_DECRYPT_EXISTING_ENTRY;
385 } else {
386 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
387 syncable::CREATE,
388 model_type, parent_id, dummy);
389 }
390
391 if (!entry_->good())
392 return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
393
394 // Has no impact if the client tag is already set.
395 entry_->PutUniqueClientTag(hash);
396
397 // We don't support directory and tag combinations.
398 entry_->PutIsDir(false);
399
400 if (entry_->ShouldMaintainPosition()) {
401 if (!entry_->PutPredecessor(syncable::Id()))
402 return INIT_FAILED_SET_PREDECESSOR;
403 }
404
405 // Mark this entry as unsynced, to wake up the syncer.
406 MarkForSyncing();
407
408 return INIT_SUCCESS;
409 }
410
411 bool WriteNode::SetPosition(const BaseNode& new_parent,
412 const BaseNode* predecessor) {
413 // |predecessor| must be a child of |new_parent| or NULL.
414 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
415 DCHECK(false);
416 return false;
417 }
418
419 syncable::Id new_parent_id = new_parent.GetSyncId();
420 DCHECK(!new_parent_id.IsNull());
421
422 // Filter out redundant changes if both the parent and the predecessor match.
423 if (new_parent_id == entry_->GetParentId()) {
424 const syncable::Id& old = entry_->GetPredecessorId();
425 if ((!predecessor && old.IsNull()) ||
426 (predecessor && (old == predecessor->GetSyncId()))) {
427 return true;
428 }
429 }
430
431 entry_->PutParentId(new_parent_id);
432
433 if (!PutPredecessor(predecessor)) {
434 return false;
435 }
436
437 // Mark this entry as unsynced, to wake up the syncer.
438 MarkForSyncing();
439 return true;
440 }
441
442 void WriteNode::SetAttachmentMetadata(
443 const sync_pb::AttachmentMetadata& attachment_metadata) {
444 entry_->PutAttachmentMetadata(attachment_metadata);
445 }
446
447 const syncable::Entry* WriteNode::GetEntry() const {
448 return entry_;
449 }
450
451 const BaseTransaction* WriteNode::GetTransaction() const {
452 return transaction_;
453 }
454
455 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
456 return entry_;
457 }
458
459 void WriteNode::Tombstone() {
460 // These lines must be in this order. The call to Put(IS_DEL) might choose to
461 // unset the IS_UNSYNCED bit if the item was not known to the server at the
462 // time of deletion. It's important that the bit not be reset in that case.
463 MarkForSyncing();
464 entry_->PutIsDel(true);
465 }
466
467 void WriteNode::Drop() {
468 if (entry_->GetId().ServerKnows()) {
469 entry_->PutIsDel(true);
470 }
471 }
472
473 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
474 DCHECK(!entry_->GetParentId().IsNull());
475 syncable::Id predecessor_id =
476 predecessor ? predecessor->GetSyncId() : syncable::Id();
477 return entry_->PutPredecessor(predecessor_id);
478 }
479
480 void WriteNode::MarkForSyncing() {
481 syncable::MarkForSyncing(entry_);
482 }
483
484 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/internal_api/user_share.cc ('k') | sync/internal_api/write_transaction.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698