Home | History | Annotate | Download | only in syncable
      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