1 // Copyright 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/syncable/mutable_entry.h" 6 7 #include "base/memory/scoped_ptr.h" 8 #include "sync/internal_api/public/base/unique_position.h" 9 #include "sync/syncable/directory.h" 10 #include "sync/syncable/scoped_kernel_lock.h" 11 #include "sync/syncable/scoped_parent_child_index_updater.h" 12 #include "sync/syncable/syncable-inl.h" 13 #include "sync/syncable/syncable_changes_version.h" 14 #include "sync/syncable/syncable_util.h" 15 #include "sync/syncable/syncable_write_transaction.h" 16 17 using std::string; 18 19 namespace syncer { 20 namespace syncable { 21 22 void MutableEntry::Init(WriteTransaction* trans, 23 ModelType model_type, 24 const Id& parent_id, 25 const string& name) { 26 scoped_ptr<EntryKernel> kernel(new EntryKernel); 27 kernel_ = NULL; 28 29 kernel->put(ID, trans->directory_->NextId()); 30 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 31 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 32 kernel->put(PARENT_ID, parent_id); 33 kernel->put(NON_UNIQUE_NAME, name); 34 const base::Time& now = base::Time::Now(); 35 kernel->put(CTIME, now); 36 kernel->put(MTIME, now); 37 // We match the database defaults here 38 kernel->put(BASE_VERSION, CHANGES_VERSION); 39 40 // Normally the SPECIFICS setting code is wrapped in logic to deal with 41 // unknown fields and encryption. Since all we want to do here is ensure that 42 // GetModelType() returns a correct value from the very beginning, these 43 // few lines are sufficient. 44 sync_pb::EntitySpecifics specifics; 45 AddDefaultFieldValue(model_type, &specifics); 46 kernel->put(SPECIFICS, specifics); 47 48 // Because this entry is new, it was originally deleted. 49 kernel->put(IS_DEL, true); 50 trans->SaveOriginal(kernel.get()); 51 kernel->put(IS_DEL, false); 52 53 // Now swap the pointers. 54 kernel_ = kernel.release(); 55 } 56 57 MutableEntry::MutableEntry(WriteTransaction* trans, 58 Create, 59 ModelType model_type, 60 const Id& parent_id, 61 const string& name) 62 : Entry(trans), 63 write_transaction_(trans) { 64 Init(trans, model_type, parent_id, name); 65 // We need to have a valid position ready before we can index the item. 66 if (model_type == BOOKMARKS) { 67 // Base the tag off of our cache-guid and local "c-" style ID. 68 std::string unique_tag = syncable::GenerateSyncableBookmarkHash( 69 trans->directory()->cache_guid(), Get(ID).GetServerId()); 70 kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag); 71 kernel_->put(UNIQUE_POSITION, UniquePosition::InitialPosition(unique_tag)); 72 } else { 73 DCHECK(!ShouldMaintainPosition()); 74 } 75 76 bool result = trans->directory()->InsertEntry(trans, kernel_); 77 DCHECK(result); 78 } 79 80 MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, 81 const Id& id) 82 : Entry(trans), write_transaction_(trans) { 83 Entry same_id(trans, GET_BY_ID, id); 84 kernel_ = NULL; 85 if (same_id.good()) { 86 return; // already have an item with this ID. 87 } 88 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 89 90 kernel->put(ID, id); 91 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 92 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 93 kernel->put(IS_DEL, true); 94 // We match the database defaults here 95 kernel->put(BASE_VERSION, CHANGES_VERSION); 96 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 97 return; // Failed inserting. 98 } 99 trans->SaveOriginal(kernel.get()); 100 101 kernel_ = kernel.release(); 102 } 103 104 MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) 105 : Entry(trans, GET_BY_ID, id), write_transaction_(trans) { 106 } 107 108 MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, 109 int64 metahandle) 110 : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) { 111 } 112 113 MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, 114 const std::string& tag) 115 : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) { 116 } 117 118 MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, 119 const string& tag) 120 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { 121 } 122 123 bool MutableEntry::PutIsDel(bool is_del) { 124 DCHECK(kernel_); 125 write_transaction_->SaveOriginal(kernel_); 126 if (is_del == kernel_->ref(IS_DEL)) { 127 return true; 128 } 129 if (is_del) { 130 // If the server never knew about this item and it's deleted then we don't 131 // need to keep it around. Unsetting IS_UNSYNCED will: 132 // - Ensure that the item is never committed to the server. 133 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other 134 // clients to override this entry. 135 // - Let us delete this entry permanently through 136 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. 137 // This will save memory and avoid crbug.com/125381. 138 if (!Get(ID).ServerKnows()) { 139 Put(IS_UNSYNCED, false); 140 } 141 } 142 143 { 144 ScopedKernelLock lock(dir()); 145 // Some indices don't include deleted items and must be updated 146 // upon a value change. 147 ScopedParentChildIndexUpdater updater(lock, kernel_, 148 &dir()->kernel_->parent_child_index); 149 150 kernel_->put(IS_DEL, is_del); 151 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 152 } 153 154 return true; 155 } 156 157 bool MutableEntry::Put(Int64Field field, const int64& value) { 158 DCHECK(kernel_); 159 160 // We shouldn't set TRANSACTION_VERSION here. See UpdateTransactionVersion. 161 DCHECK_NE(TRANSACTION_VERSION, field); 162 163 write_transaction_->SaveOriginal(kernel_); 164 if (kernel_->ref(field) != value) { 165 ScopedKernelLock lock(dir()); 166 kernel_->put(field, value); 167 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 168 } 169 return true; 170 } 171 172 bool MutableEntry::Put(TimeField field, const base::Time& value) { 173 DCHECK(kernel_); 174 write_transaction_->SaveOriginal(kernel_); 175 if (kernel_->ref(field) != value) { 176 kernel_->put(field, value); 177 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 178 } 179 return true; 180 } 181 182 bool MutableEntry::Put(IdField field, const Id& value) { 183 DCHECK(kernel_); 184 write_transaction_->SaveOriginal(kernel_); 185 if (kernel_->ref(field) != value) { 186 if (ID == field) { 187 if (!dir()->ReindexId(write_transaction(), kernel_, value)) 188 return false; 189 } else if (PARENT_ID == field) { 190 PutParentIdPropertyOnly(value); 191 if (!Get(IS_DEL)) { 192 if (!PutPredecessor(Id())) { 193 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 194 NOTREACHED(); 195 } 196 } 197 } else { 198 kernel_->put(field, value); 199 } 200 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 201 } 202 return true; 203 } 204 205 bool MutableEntry::Put(UniquePositionField field, const UniquePosition& value) { 206 DCHECK(kernel_); 207 write_transaction_->SaveOriginal(kernel_); 208 if(!kernel_->ref(field).Equals(value)) { 209 // We should never overwrite a valid position with an invalid one. 210 DCHECK(value.IsValid()); 211 ScopedKernelLock lock(dir()); 212 if (UNIQUE_POSITION == field) { 213 ScopedParentChildIndexUpdater updater( 214 lock, kernel_, &dir()->kernel_->parent_child_index); 215 kernel_->put(field, value); 216 } else { 217 kernel_->put(field, value); 218 } 219 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 220 } 221 return true; 222 } 223 224 void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { 225 write_transaction_->SaveOriginal(kernel_); 226 dir()->ReindexParentId(write_transaction(), kernel_, parent_id); 227 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 228 } 229 230 bool MutableEntry::Put(BaseVersion field, int64 value) { 231 DCHECK(kernel_); 232 write_transaction_->SaveOriginal(kernel_); 233 if (kernel_->ref(field) != value) { 234 kernel_->put(field, value); 235 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 236 } 237 return true; 238 } 239 240 bool MutableEntry::Put(StringField field, const string& value) { 241 DCHECK(kernel_); 242 write_transaction_->SaveOriginal(kernel_); 243 if (field == UNIQUE_CLIENT_TAG) { 244 return PutUniqueClientTag(value); 245 } 246 247 if (field == UNIQUE_SERVER_TAG) { 248 return PutUniqueServerTag(value); 249 } 250 251 DCHECK_NE(UNIQUE_BOOKMARK_TAG, field) 252 << "Should use PutUniqueBookmarkTag instead of Put(UNIQUE_BOOKMARK_TAG)"; 253 254 if (kernel_->ref(field) != value) { 255 kernel_->put(field, value); 256 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 257 } 258 return true; 259 } 260 261 bool MutableEntry::Put(ProtoField field, 262 const sync_pb::EntitySpecifics& value) { 263 DCHECK(kernel_); 264 CHECK(!value.password().has_client_only_encrypted_data()); 265 write_transaction_->SaveOriginal(kernel_); 266 // TODO(ncarter): This is unfortunately heavyweight. Can we do 267 // better? 268 if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) { 269 const bool update_unapplied_updates_index = 270 (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE); 271 if (update_unapplied_updates_index) { 272 // Remove ourselves from unapplied_update_metahandles with our 273 // old server type. 274 const ModelType old_server_type = kernel_->GetServerModelType(); 275 const int64 metahandle = kernel_->ref(META_HANDLE); 276 size_t erase_count = 277 dir()->kernel_->unapplied_update_metahandles[old_server_type] 278 .erase(metahandle); 279 DCHECK_EQ(erase_count, 1u); 280 } 281 282 kernel_->put(field, value); 283 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 284 285 if (update_unapplied_updates_index) { 286 // Add ourselves back into unapplied_update_metahandles with our 287 // new server type. 288 const ModelType new_server_type = kernel_->GetServerModelType(); 289 const int64 metahandle = kernel_->ref(META_HANDLE); 290 dir()->kernel_->unapplied_update_metahandles[new_server_type] 291 .insert(metahandle); 292 } 293 } 294 return true; 295 } 296 297 bool MutableEntry::Put(BitField field, bool value) { 298 DCHECK(kernel_); 299 write_transaction_->SaveOriginal(kernel_); 300 bool old_value = kernel_->ref(field); 301 if (old_value != value) { 302 kernel_->put(field, value); 303 kernel_->mark_dirty(GetDirtyIndexHelper()); 304 } 305 306 // Update delete journal for existence status change on server side here 307 // instead of in PutIsDel() because IS_DEL may not be updated due to 308 // early returns when processing updates. And because 309 // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has 310 // to be called on sync thread. 311 if (field == SERVER_IS_DEL) { 312 dir()->delete_journal()->UpdateDeleteJournalForServerDelete( 313 write_transaction(), old_value, *kernel_); 314 } 315 316 return true; 317 } 318 319 MetahandleSet* MutableEntry::GetDirtyIndexHelper() { 320 return &dir()->kernel_->dirty_metahandles; 321 } 322 323 bool MutableEntry::PutUniqueClientTag(const string& new_tag) { 324 if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) { 325 return true; 326 } 327 328 write_transaction_->SaveOriginal(kernel_); 329 ScopedKernelLock lock(dir()); 330 // Make sure your new value is not in there already. 331 if (dir()->kernel_->client_tags_map.find(new_tag) != 332 dir()->kernel_->client_tags_map.end()) { 333 DVLOG(1) << "Detected duplicate client tag"; 334 return false; 335 } 336 dir()->kernel_->client_tags_map.erase( 337 kernel_->ref(UNIQUE_CLIENT_TAG)); 338 kernel_->put(UNIQUE_CLIENT_TAG, new_tag); 339 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 340 if (!new_tag.empty()) { 341 dir()->kernel_->client_tags_map[new_tag] = kernel_; 342 } 343 344 return true; 345 } 346 347 bool MutableEntry::PutUniqueServerTag(const string& new_tag) { 348 if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) { 349 return true; 350 } 351 352 write_transaction_->SaveOriginal(kernel_); 353 ScopedKernelLock lock(dir()); 354 // Make sure your new value is not in there already. 355 if (dir()->kernel_->server_tags_map.find(new_tag) != 356 dir()->kernel_->server_tags_map.end()) { 357 DVLOG(1) << "Detected duplicate server tag"; 358 return false; 359 } 360 dir()->kernel_->server_tags_map.erase( 361 kernel_->ref(UNIQUE_SERVER_TAG)); 362 kernel_->put(UNIQUE_SERVER_TAG, new_tag); 363 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 364 if (!new_tag.empty()) { 365 dir()->kernel_->server_tags_map[new_tag] = kernel_; 366 } 367 368 return true; 369 } 370 371 bool MutableEntry::Put(IndexedBitField field, bool value) { 372 DCHECK(kernel_); 373 write_transaction_->SaveOriginal(kernel_); 374 if (kernel_->ref(field) != value) { 375 MetahandleSet* index; 376 if (IS_UNSYNCED == field) { 377 index = &dir()->kernel_->unsynced_metahandles; 378 } else { 379 // Use kernel_->GetServerModelType() instead of 380 // GetServerModelType() as we may trigger some DCHECKs in the 381 // latter. 382 index = 383 &dir()->kernel_->unapplied_update_metahandles[ 384 kernel_->GetServerModelType()]; 385 } 386 387 ScopedKernelLock lock(dir()); 388 if (value) { 389 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, 390 FROM_HERE, 391 "Could not insert", 392 write_transaction())) { 393 return false; 394 } 395 } else { 396 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), 397 FROM_HERE, 398 "Entry Not succesfully erased", 399 write_transaction())) { 400 return false; 401 } 402 } 403 kernel_->put(field, value); 404 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 405 } 406 return true; 407 } 408 409 void MutableEntry::PutUniqueBookmarkTag(const std::string& tag) { 410 // This unique tag will eventually be used as the unique suffix when adjusting 411 // this bookmark's position. Let's make sure it's a valid suffix. 412 if (!UniquePosition::IsValidSuffix(tag)) { 413 NOTREACHED(); 414 return; 415 } 416 417 if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty() 418 && tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) { 419 // There is only one scenario where our tag is expected to change. That 420 // scenario occurs when our current tag is a non-correct tag assigned during 421 // the UniquePosition migration. 422 std::string migration_generated_tag = 423 GenerateSyncableBookmarkHash(std::string(), 424 kernel_->ref(ID).GetServerId()); 425 DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG)); 426 } 427 428 kernel_->put(UNIQUE_BOOKMARK_TAG, tag); 429 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 430 } 431 432 bool MutableEntry::PutPredecessor(const Id& predecessor_id) { 433 MutableEntry predecessor(write_transaction_, GET_BY_ID, predecessor_id); 434 if (!predecessor.good()) 435 return false; 436 dir()->PutPredecessor(kernel_, predecessor.kernel_); 437 return true; 438 } 439 440 bool MutableEntry::Put(BitTemp field, bool value) { 441 DCHECK(kernel_); 442 kernel_->put(field, value); 443 return true; 444 } 445 446 void MutableEntry::UpdateTransactionVersion(int64 value) { 447 ScopedKernelLock lock(dir()); 448 kernel_->put(TRANSACTION_VERSION, value); 449 kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles)); 450 } 451 452 // This function sets only the flags needed to get this entry to sync. 453 bool MarkForSyncing(MutableEntry* e) { 454 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); 455 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; 456 if (!(e->Put(IS_UNSYNCED, true))) 457 return false; 458 e->Put(SYNCING, false); 459 return true; 460 } 461 462 } // namespace syncable 463 } // namespace syncer 464