1 // Copyright 2013 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/model_neutral_mutable_entry.h" 6 7 #include <string> 8 9 #include "sync/internal_api/public/base/unique_position.h" 10 #include "sync/syncable/directory.h" 11 #include "sync/syncable/scoped_kernel_lock.h" 12 #include "sync/syncable/syncable_changes_version.h" 13 #include "sync/syncable/syncable_util.h" 14 #include "sync/syncable/syncable_write_transaction.h" 15 16 using std::string; 17 18 namespace syncer { 19 20 namespace syncable { 21 22 ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans, 23 CreateNewUpdateItem, 24 const Id& id) 25 : Entry(trans), base_write_transaction_(trans) { 26 Entry same_id(trans, GET_BY_ID, id); 27 kernel_ = NULL; 28 if (same_id.good()) { 29 return; // already have an item with this ID. 30 } 31 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 32 33 kernel->put(ID, id); 34 kernel->put(META_HANDLE, trans->directory()->NextMetahandle()); 35 kernel->mark_dirty(&trans->directory()->kernel_->dirty_metahandles); 36 kernel->put(IS_DEL, true); 37 // We match the database defaults here 38 kernel->put(BASE_VERSION, CHANGES_VERSION); 39 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 40 return; // Failed inserting. 41 } 42 trans->TrackChangesTo(kernel.get()); 43 44 kernel_ = kernel.release(); 45 } 46 47 ModelNeutralMutableEntry::ModelNeutralMutableEntry( 48 BaseWriteTransaction* trans, GetById, const Id& id) 49 : Entry(trans, GET_BY_ID, id), base_write_transaction_(trans) { 50 } 51 52 ModelNeutralMutableEntry::ModelNeutralMutableEntry( 53 BaseWriteTransaction* trans, GetByHandle, int64 metahandle) 54 : Entry(trans, GET_BY_HANDLE, metahandle), base_write_transaction_(trans) { 55 } 56 57 ModelNeutralMutableEntry::ModelNeutralMutableEntry( 58 BaseWriteTransaction* trans, GetByClientTag, const std::string& tag) 59 : Entry(trans, GET_BY_CLIENT_TAG, tag), base_write_transaction_(trans) { 60 } 61 62 ModelNeutralMutableEntry::ModelNeutralMutableEntry( 63 BaseWriteTransaction* trans, GetByServerTag, const string& tag) 64 : Entry(trans, GET_BY_SERVER_TAG, tag), base_write_transaction_(trans) { 65 } 66 67 void ModelNeutralMutableEntry::PutBaseVersion(int64 value) { 68 DCHECK(kernel_); 69 base_write_transaction_->TrackChangesTo(kernel_); 70 if (kernel_->ref(BASE_VERSION) != value) { 71 kernel_->put(BASE_VERSION, value); 72 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 73 } 74 } 75 76 void ModelNeutralMutableEntry::PutServerVersion(int64 value) { 77 DCHECK(kernel_); 78 base_write_transaction_->TrackChangesTo(kernel_); 79 if (kernel_->ref(SERVER_VERSION) != value) { 80 ScopedKernelLock lock(dir()); 81 kernel_->put(SERVER_VERSION, value); 82 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 83 } 84 } 85 86 void ModelNeutralMutableEntry::PutServerMtime(base::Time value) { 87 DCHECK(kernel_); 88 base_write_transaction_->TrackChangesTo(kernel_); 89 if (kernel_->ref(SERVER_MTIME) != value) { 90 kernel_->put(SERVER_MTIME, value); 91 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 92 } 93 } 94 95 void ModelNeutralMutableEntry::PutServerCtime(base::Time value) { 96 DCHECK(kernel_); 97 base_write_transaction_->TrackChangesTo(kernel_); 98 if (kernel_->ref(SERVER_CTIME) != value) { 99 kernel_->put(SERVER_CTIME, value); 100 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 101 } 102 } 103 104 bool ModelNeutralMutableEntry::PutId(const Id& value) { 105 DCHECK(kernel_); 106 base_write_transaction_->TrackChangesTo(kernel_); 107 if (kernel_->ref(ID) != value) { 108 if (!dir()->ReindexId(base_write_transaction(), kernel_, value)) 109 return false; 110 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 111 } 112 return true; 113 } 114 115 void ModelNeutralMutableEntry::PutServerParentId(const Id& value) { 116 DCHECK(kernel_); 117 base_write_transaction_->TrackChangesTo(kernel_); 118 119 if (kernel_->ref(SERVER_PARENT_ID) != value) { 120 kernel_->put(SERVER_PARENT_ID, value); 121 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 122 } 123 } 124 125 bool ModelNeutralMutableEntry::PutIsUnsynced(bool value) { 126 DCHECK(kernel_); 127 base_write_transaction_->TrackChangesTo(kernel_); 128 if (kernel_->ref(IS_UNSYNCED) != value) { 129 MetahandleSet* index = &dir()->kernel_->unsynced_metahandles; 130 131 ScopedKernelLock lock(dir()); 132 if (value) { 133 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, 134 FROM_HERE, 135 "Could not insert", 136 base_write_transaction())) { 137 return false; 138 } 139 } else { 140 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), 141 FROM_HERE, 142 "Entry Not succesfully erased", 143 base_write_transaction())) { 144 return false; 145 } 146 } 147 kernel_->put(IS_UNSYNCED, value); 148 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 149 } 150 return true; 151 } 152 153 bool ModelNeutralMutableEntry::PutIsUnappliedUpdate(bool value) { 154 DCHECK(kernel_); 155 base_write_transaction_->TrackChangesTo(kernel_); 156 if (kernel_->ref(IS_UNAPPLIED_UPDATE) != value) { 157 // Use kernel_->GetServerModelType() instead of 158 // GetServerModelType() as we may trigger some DCHECKs in the 159 // latter. 160 MetahandleSet* index = &dir()->kernel_->unapplied_update_metahandles[ 161 kernel_->GetServerModelType()]; 162 163 ScopedKernelLock lock(dir()); 164 if (value) { 165 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, 166 FROM_HERE, 167 "Could not insert", 168 base_write_transaction())) { 169 return false; 170 } 171 } else { 172 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), 173 FROM_HERE, 174 "Entry Not succesfully erased", 175 base_write_transaction())) { 176 return false; 177 } 178 } 179 kernel_->put(IS_UNAPPLIED_UPDATE, value); 180 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 181 } 182 return true; 183 } 184 185 void ModelNeutralMutableEntry::PutServerIsDir(bool value) { 186 DCHECK(kernel_); 187 base_write_transaction_->TrackChangesTo(kernel_); 188 bool old_value = kernel_->ref(SERVER_IS_DIR); 189 if (old_value != value) { 190 kernel_->put(SERVER_IS_DIR, value); 191 kernel_->mark_dirty(GetDirtyIndexHelper()); 192 } 193 } 194 195 void ModelNeutralMutableEntry::PutServerIsDel(bool value) { 196 DCHECK(kernel_); 197 base_write_transaction_->TrackChangesTo(kernel_); 198 bool old_value = kernel_->ref(SERVER_IS_DEL); 199 if (old_value != value) { 200 kernel_->put(SERVER_IS_DEL, value); 201 kernel_->mark_dirty(GetDirtyIndexHelper()); 202 } 203 204 // Update delete journal for existence status change on server side here 205 // instead of in PutIsDel() because IS_DEL may not be updated due to 206 // early returns when processing updates. And because 207 // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has 208 // to be called on sync thread. 209 dir()->delete_journal()->UpdateDeleteJournalForServerDelete( 210 base_write_transaction(), old_value, *kernel_); 211 } 212 213 void ModelNeutralMutableEntry::PutServerNonUniqueName( 214 const std::string& value) { 215 DCHECK(kernel_); 216 base_write_transaction_->TrackChangesTo(kernel_); 217 218 if (kernel_->ref(SERVER_NON_UNIQUE_NAME) != value) { 219 kernel_->put(SERVER_NON_UNIQUE_NAME, value); 220 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 221 } 222 } 223 224 bool ModelNeutralMutableEntry::PutUniqueServerTag(const string& new_tag) { 225 if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) { 226 return true; 227 } 228 229 base_write_transaction_->TrackChangesTo(kernel_); 230 ScopedKernelLock lock(dir()); 231 // Make sure your new value is not in there already. 232 if (dir()->kernel_->server_tags_map.find(new_tag) != 233 dir()->kernel_->server_tags_map.end()) { 234 DVLOG(1) << "Detected duplicate server tag"; 235 return false; 236 } 237 dir()->kernel_->server_tags_map.erase( 238 kernel_->ref(UNIQUE_SERVER_TAG)); 239 kernel_->put(UNIQUE_SERVER_TAG, new_tag); 240 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 241 if (!new_tag.empty()) { 242 dir()->kernel_->server_tags_map[new_tag] = kernel_; 243 } 244 245 return true; 246 } 247 248 bool ModelNeutralMutableEntry::PutUniqueClientTag(const string& new_tag) { 249 if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) { 250 return true; 251 } 252 253 base_write_transaction_->TrackChangesTo(kernel_); 254 ScopedKernelLock lock(dir()); 255 // Make sure your new value is not in there already. 256 if (dir()->kernel_->client_tags_map.find(new_tag) != 257 dir()->kernel_->client_tags_map.end()) { 258 DVLOG(1) << "Detected duplicate client tag"; 259 return false; 260 } 261 dir()->kernel_->client_tags_map.erase( 262 kernel_->ref(UNIQUE_CLIENT_TAG)); 263 kernel_->put(UNIQUE_CLIENT_TAG, new_tag); 264 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 265 if (!new_tag.empty()) { 266 dir()->kernel_->client_tags_map[new_tag] = kernel_; 267 } 268 269 return true; 270 } 271 272 void ModelNeutralMutableEntry::PutUniqueBookmarkTag(const std::string& tag) { 273 // This unique tag will eventually be used as the unique suffix when adjusting 274 // this bookmark's position. Let's make sure it's a valid suffix. 275 if (!UniquePosition::IsValidSuffix(tag)) { 276 NOTREACHED(); 277 return; 278 } 279 280 if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty() && 281 tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) { 282 // There is only one scenario where our tag is expected to change. That 283 // scenario occurs when our current tag is a non-correct tag assigned during 284 // the UniquePosition migration. 285 std::string migration_generated_tag = 286 GenerateSyncableBookmarkHash(std::string(), 287 kernel_->ref(ID).GetServerId()); 288 DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG)); 289 } 290 291 kernel_->put(UNIQUE_BOOKMARK_TAG, tag); 292 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 293 } 294 295 void ModelNeutralMutableEntry::PutServerSpecifics( 296 const sync_pb::EntitySpecifics& value) { 297 DCHECK(kernel_); 298 CHECK(!value.password().has_client_only_encrypted_data()); 299 base_write_transaction_->TrackChangesTo(kernel_); 300 // TODO(ncarter): This is unfortunately heavyweight. Can we do 301 // better? 302 if (kernel_->ref(SERVER_SPECIFICS).SerializeAsString() != 303 value.SerializeAsString()) { 304 if (kernel_->ref(IS_UNAPPLIED_UPDATE)) { 305 // Remove ourselves from unapplied_update_metahandles with our 306 // old server type. 307 const ModelType old_server_type = kernel_->GetServerModelType(); 308 const int64 metahandle = kernel_->ref(META_HANDLE); 309 size_t erase_count = 310 dir()->kernel_->unapplied_update_metahandles[old_server_type] 311 .erase(metahandle); 312 DCHECK_EQ(erase_count, 1u); 313 } 314 315 kernel_->put(SERVER_SPECIFICS, value); 316 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 317 318 if (kernel_->ref(IS_UNAPPLIED_UPDATE)) { 319 // Add ourselves back into unapplied_update_metahandles with our 320 // new server type. 321 const ModelType new_server_type = kernel_->GetServerModelType(); 322 const int64 metahandle = kernel_->ref(META_HANDLE); 323 dir()->kernel_->unapplied_update_metahandles[new_server_type] 324 .insert(metahandle); 325 } 326 } 327 } 328 329 void ModelNeutralMutableEntry::PutBaseServerSpecifics( 330 const sync_pb::EntitySpecifics& value) { 331 DCHECK(kernel_); 332 CHECK(!value.password().has_client_only_encrypted_data()); 333 base_write_transaction_->TrackChangesTo(kernel_); 334 // TODO(ncarter): This is unfortunately heavyweight. Can we do 335 // better? 336 if (kernel_->ref(BASE_SERVER_SPECIFICS).SerializeAsString() 337 != value.SerializeAsString()) { 338 kernel_->put(BASE_SERVER_SPECIFICS, value); 339 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 340 } 341 } 342 343 void ModelNeutralMutableEntry::PutServerUniquePosition( 344 const UniquePosition& value) { 345 DCHECK(kernel_); 346 base_write_transaction_->TrackChangesTo(kernel_); 347 if(!kernel_->ref(SERVER_UNIQUE_POSITION).Equals(value)) { 348 // We should never overwrite a valid position with an invalid one. 349 DCHECK(value.IsValid()); 350 ScopedKernelLock lock(dir()); 351 kernel_->put(SERVER_UNIQUE_POSITION, value); 352 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 353 } 354 } 355 356 void ModelNeutralMutableEntry::PutSyncing(bool value) { 357 kernel_->put(SYNCING, value); 358 } 359 360 void ModelNeutralMutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { 361 base_write_transaction_->TrackChangesTo(kernel_); 362 dir()->ReindexParentId(base_write_transaction(), kernel_, parent_id); 363 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 364 } 365 366 void ModelNeutralMutableEntry::UpdateTransactionVersion(int64 value) { 367 ScopedKernelLock lock(dir()); 368 kernel_->put(TRANSACTION_VERSION, value); 369 kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles)); 370 } 371 372 ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans) 373 : Entry(trans), base_write_transaction_(trans) {} 374 375 MetahandleSet* ModelNeutralMutableEntry::GetDirtyIndexHelper() { 376 return &dir()->kernel_->dirty_metahandles; 377 } 378 379 } // namespace syncable 380 381 } // namespace syncer 382