This source file includes following definitions.
- reset_download_progress
- transaction_observer
- invariant_check_level_
- InitializeIndices
- OpenImpl
- delete_journal
- Close
- OnUnrecoverableError
- GetEntryById
- GetEntryById
- GetEntryByClientTag
- GetEntryByServerTag
- GetEntryByHandle
- GetEntryByHandle
- GetChildHandlesById
- GetTotalNodeCount
- GetChildSetForKernel
- GetPositionIndex
- GetRootEntry
- InsertEntry
- InsertEntry
- ReindexId
- ReindexParentId
- unrecoverable_error_set
- ClearDirtyMetahandles
- SafeToPurgeFromMemory
- TakeSnapshotForSaveChanges
- SaveChanges
- VacuumAfterSaveChanges
- UnapplyEntry
- DeleteEntry
- PurgeEntriesWithTypeIn
- HandleSaveChangesFailure
- GetDownloadProgress
- GetDownloadProgressAsString
- GetEntriesCount
- SetDownloadProgress
- GetTransactionVersion
- IncrementTransactionVersion
- GetDataTypeContext
- SetDataTypeContext
- InitialSyncEndedTypes
- InitialSyncEndedForType
- InitialSyncEndedForType
- store_birthday
- set_store_birthday
- bag_of_chips
- set_bag_of_chips
- cache_guid
- GetNigoriHandler
- GetCryptographer
- GetAllMetaHandles
- GetUnsyncedMetaHandles
- unsynced_entity_count
- TypeHasUnappliedUpdates
- GetUnappliedUpdateMetaHandles
- GetMetaHandlesOfType
- CollectMetaHandleCounts
- GetAllNodeDetails
- CheckInvariantsOnTransactionClose
- FullyCheckTreeInvariants
- CheckTreeInvariants
- SetInvariantCheckLevel
- NextMetahandle
- NextId
- HasChildren
- GetFirstChildId
- GetPredecessorId
- GetSuccessorId
- PutPredecessor
- AppendChildHandles
#include "sync/syncable/directory.h"
#include <iterator>
#include "base/base64.h"
#include "base/debug/trace_event.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "sync/internal_api/public/base/unique_position.h"
#include "sync/internal_api/public/util/unrecoverable_error_handler.h"
#include "sync/syncable/entry.h"
#include "sync/syncable/entry_kernel.h"
#include "sync/syncable/in_memory_directory_backing_store.h"
#include "sync/syncable/on_disk_directory_backing_store.h"
#include "sync/syncable/scoped_kernel_lock.h"
#include "sync/syncable/scoped_parent_child_index_updater.h"
#include "sync/syncable/syncable-inl.h"
#include "sync/syncable/syncable_base_transaction.h"
#include "sync/syncable/syncable_changes_version.h"
#include "sync/syncable/syncable_read_transaction.h"
#include "sync/syncable/syncable_util.h"
#include "sync/syncable/syncable_write_transaction.h"
using std::string;
namespace syncer {
namespace syncable {
const base::FilePath::CharType Directory::kSyncDatabaseFilename[] =
FILE_PATH_LITERAL("SyncData.sqlite3");
Directory::PersistedKernelInfo::PersistedKernelInfo()
: next_id(0) {
ModelTypeSet protocol_types = ProtocolTypes();
for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
iter.Inc()) {
reset_download_progress(iter.Get());
transaction_version[iter.Get()] = 0;
}
}
Directory::PersistedKernelInfo::~PersistedKernelInfo() {}
void Directory::PersistedKernelInfo::reset_download_progress(
ModelType model_type) {
download_progress[model_type].set_data_type_id(
GetSpecificsFieldNumberFromModelType(model_type));
download_progress[model_type].set_token(std::string());
}
Directory::SaveChangesSnapshot::SaveChangesSnapshot()
: kernel_info_status(KERNEL_SHARE_INFO_INVALID) {
}
Directory::SaveChangesSnapshot::~SaveChangesSnapshot() {
STLDeleteElements(&dirty_metas);
STLDeleteElements(&delete_journals);
}
Directory::Kernel::Kernel(
const std::string& name,
const KernelLoadInfo& info, DirectoryChangeDelegate* delegate,
const WeakHandle<TransactionObserver>& transaction_observer)
: next_write_transaction_id(0),
name(name),
info_status(Directory::KERNEL_SHARE_INFO_VALID),
persisted_info(info.kernel_info),
cache_guid(info.cache_guid),
next_metahandle(info.max_metahandle + 1),
delegate(delegate),
transaction_observer(transaction_observer) {
DCHECK(delegate);
DCHECK(transaction_observer.IsInitialized());
}
Directory::Kernel::~Kernel() {
STLDeleteContainerPairSecondPointers(metahandles_map.begin(),
metahandles_map.end());
}
Directory::Directory(
DirectoryBackingStore* store,
UnrecoverableErrorHandler* unrecoverable_error_handler,
ReportUnrecoverableErrorFunction report_unrecoverable_error_function,
NigoriHandler* nigori_handler,
Cryptographer* cryptographer)
: kernel_(NULL),
store_(store),
unrecoverable_error_handler_(unrecoverable_error_handler),
report_unrecoverable_error_function_(
report_unrecoverable_error_function),
unrecoverable_error_set_(false),
nigori_handler_(nigori_handler),
cryptographer_(cryptographer),
invariant_check_level_(VERIFY_CHANGES) {
}
Directory::~Directory() {
Close();
}
DirOpenResult Directory::Open(
const string& name,
DirectoryChangeDelegate* delegate,
const WeakHandle<TransactionObserver>& transaction_observer) {
TRACE_EVENT0("sync", "SyncDatabaseOpen");
const DirOpenResult result =
OpenImpl(name, delegate, transaction_observer);
if (OPENED != result)
Close();
return result;
}
void Directory::InitializeIndices(MetahandlesMap* handles_map) {
kernel_->metahandles_map.swap(*handles_map);
for (MetahandlesMap::const_iterator it = kernel_->metahandles_map.begin();
it != kernel_->metahandles_map.end(); ++it) {
EntryKernel* entry = it->second;
if (ParentChildIndex::ShouldInclude(entry))
kernel_->parent_child_index.Insert(entry);
const int64 metahandle = entry->ref(META_HANDLE);
if (entry->ref(IS_UNSYNCED))
kernel_->unsynced_metahandles.insert(metahandle);
if (entry->ref(IS_UNAPPLIED_UPDATE)) {
const ModelType type = entry->GetServerModelType();
kernel_->unapplied_update_metahandles[type].insert(metahandle);
}
if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) ==
kernel_->server_tags_map.end())
<< "Unexpected duplicate use of client tag";
kernel_->server_tags_map[entry->ref(UNIQUE_SERVER_TAG)] = entry;
}
if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) ==
kernel_->server_tags_map.end())
<< "Unexpected duplicate use of server tag";
kernel_->client_tags_map[entry->ref(UNIQUE_CLIENT_TAG)] = entry;
}
DCHECK(kernel_->ids_map.find(entry->ref(ID).value()) ==
kernel_->ids_map.end()) << "Unexpected duplicate use of ID";
kernel_->ids_map[entry->ref(ID).value()] = entry;
DCHECK(!entry->is_dirty());
}
}
DirOpenResult Directory::OpenImpl(
const string& name,
DirectoryChangeDelegate* delegate,
const WeakHandle<TransactionObserver>&
transaction_observer) {
KernelLoadInfo info;
Directory::MetahandlesMap tmp_handles_map;
JournalIndex delete_journals;
DirOpenResult result =
store_->Load(&tmp_handles_map, &delete_journals, &info);
if (OPENED != result)
return result;
kernel_ = new Kernel(name, info, delegate, transaction_observer);
delete_journal_.reset(new DeleteJournal(&delete_journals));
InitializeIndices(&tmp_handles_map);
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
if (!SaveChanges())
return FAILED_INITIAL_WRITE;
return OPENED;
}
DeleteJournal* Directory::delete_journal() {
DCHECK(delete_journal_.get());
return delete_journal_.get();
}
void Directory::Close() {
store_.reset();
if (kernel_) {
delete kernel_;
kernel_ = NULL;
}
}
void Directory::OnUnrecoverableError(const BaseTransaction* trans,
const tracked_objects::Location& location,
const std::string & message) {
DCHECK(trans != NULL);
unrecoverable_error_set_ = true;
unrecoverable_error_handler_->OnUnrecoverableError(location,
message);
}
EntryKernel* Directory::GetEntryById(const Id& id) {
ScopedKernelLock lock(this);
return GetEntryById(id, &lock);
}
EntryKernel* Directory::GetEntryById(const Id& id,
ScopedKernelLock* const lock) {
DCHECK(kernel_);
IdsMap::iterator id_found = kernel_->ids_map.find(id.value());
if (id_found != kernel_->ids_map.end()) {
return id_found->second;
}
return NULL;
}
EntryKernel* Directory::GetEntryByClientTag(const string& tag) {
ScopedKernelLock lock(this);
DCHECK(kernel_);
TagsMap::iterator it = kernel_->client_tags_map.find(tag);
if (it != kernel_->client_tags_map.end()) {
return it->second;
}
return NULL;
}
EntryKernel* Directory::GetEntryByServerTag(const string& tag) {
ScopedKernelLock lock(this);
DCHECK(kernel_);
TagsMap::iterator it = kernel_->server_tags_map.find(tag);
if (it != kernel_->server_tags_map.end()) {
return it->second;
}
return NULL;
}
EntryKernel* Directory::GetEntryByHandle(int64 metahandle) {
ScopedKernelLock lock(this);
return GetEntryByHandle(metahandle, &lock);
}
EntryKernel* Directory::GetEntryByHandle(int64 metahandle,
ScopedKernelLock* lock) {
MetahandlesMap::iterator found =
kernel_->metahandles_map.find(metahandle);
if (found != kernel_->metahandles_map.end()) {
return found->second;
}
return NULL;
}
bool Directory::GetChildHandlesById(
BaseTransaction* trans, const Id& parent_id,
Directory::Metahandles* result) {
if (!SyncAssert(this == trans->directory(), FROM_HERE,
"Directories don't match", trans))
return false;
result->clear();
ScopedKernelLock lock(this);
AppendChildHandles(lock, parent_id, result);
return true;
}
int Directory::GetTotalNodeCount(
BaseTransaction* trans,
EntryKernel* kernel) const {
if (!SyncAssert(this == trans->directory(), FROM_HERE,
"Directories don't match", trans))
return false;
int count = 1;
std::deque<const OrderedChildSet*> child_sets;
GetChildSetForKernel(trans, kernel, &child_sets);
while (!child_sets.empty()) {
const OrderedChildSet* set = child_sets.front();
child_sets.pop_front();
for (OrderedChildSet::const_iterator it = set->begin();
it != set->end(); ++it) {
count++;
GetChildSetForKernel(trans, *it, &child_sets);
}
}
return count;
}
void Directory::GetChildSetForKernel(
BaseTransaction* trans,
EntryKernel* kernel,
std::deque<const OrderedChildSet*>* child_sets) const {
if (!kernel->ref(IS_DIR))
return;
const OrderedChildSet* descendants =
kernel_->parent_child_index.GetChildren(kernel->ref(ID));
if (!descendants)
return;
child_sets->push_back(descendants);
}
int Directory::GetPositionIndex(
BaseTransaction* trans,
EntryKernel* kernel) const {
const OrderedChildSet* siblings =
kernel_->parent_child_index.GetChildren(kernel->ref(PARENT_ID));
OrderedChildSet::const_iterator it = siblings->find(kernel);
return std::distance(siblings->begin(), it);
}
EntryKernel* Directory::GetRootEntry() {
return GetEntryById(Id());
}
bool Directory::InsertEntry(BaseWriteTransaction* trans, EntryKernel* entry) {
ScopedKernelLock lock(this);
return InsertEntry(trans, entry, &lock);
}
bool Directory::InsertEntry(BaseWriteTransaction* trans,
EntryKernel* entry,
ScopedKernelLock* lock) {
DCHECK(NULL != lock);
if (!SyncAssert(NULL != entry, FROM_HERE, "Entry is null", trans))
return false;
static const char error[] = "Entry already in memory index.";
if (!SyncAssert(
kernel_->metahandles_map.insert(
std::make_pair(entry->ref(META_HANDLE), entry)).second,
FROM_HERE,
error,
trans)) {
return false;
}
if (!SyncAssert(
kernel_->ids_map.insert(
std::make_pair(entry->ref(ID).value(), entry)).second,
FROM_HERE,
error,
trans)) {
return false;
}
if (ParentChildIndex::ShouldInclude(entry)) {
if (!SyncAssert(kernel_->parent_child_index.Insert(entry),
FROM_HERE,
error,
trans)) {
return false;
}
}
if (!SyncAssert(entry->ref(UNIQUE_SERVER_TAG).empty(), FROM_HERE,
"Server tag should be empty", trans)) {
return false;
}
if (!SyncAssert(entry->ref(UNIQUE_CLIENT_TAG).empty(), FROM_HERE,
"Client tag should be empty", trans))
return false;
return true;
}
bool Directory::ReindexId(BaseWriteTransaction* trans,
EntryKernel* const entry,
const Id& new_id) {
ScopedKernelLock lock(this);
if (NULL != GetEntryById(new_id, &lock))
return false;
{
ScopedParentChildIndexUpdater updater_b(lock, entry,
&kernel_->parent_child_index);
size_t num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
DCHECK_EQ(1U, num_erased);
entry->put(ID, new_id);
kernel_->ids_map[entry->ref(ID).value()] = entry;
}
return true;
}
bool Directory::ReindexParentId(BaseWriteTransaction* trans,
EntryKernel* const entry,
const Id& new_parent_id) {
ScopedKernelLock lock(this);
{
ScopedParentChildIndexUpdater index_updater(lock, entry,
&kernel_->parent_child_index);
entry->put(PARENT_ID, new_parent_id);
}
return true;
}
bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const {
DCHECK(trans != NULL);
return unrecoverable_error_set_;
}
void Directory::ClearDirtyMetahandles() {
kernel_->transaction_mutex.AssertAcquired();
kernel_->dirty_metahandles.clear();
}
bool Directory::SafeToPurgeFromMemory(WriteTransaction* trans,
const EntryKernel* const entry) const {
bool safe = entry->ref(IS_DEL) && !entry->is_dirty() &&
!entry->ref(SYNCING) && !entry->ref(IS_UNAPPLIED_UPDATE) &&
!entry->ref(IS_UNSYNCED);
if (safe) {
int64 handle = entry->ref(META_HANDLE);
const ModelType type = entry->GetServerModelType();
if (!SyncAssert(kernel_->dirty_metahandles.count(handle) == 0U,
FROM_HERE,
"Dirty metahandles should be empty", trans))
return false;
if (!SyncAssert(!kernel_->unsynced_metahandles.count(handle),
FROM_HERE,
"Unsynced handles should be empty",
trans))
return false;
if (!SyncAssert(!kernel_->unapplied_update_metahandles[type].count(handle),
FROM_HERE,
"Unapplied metahandles should be empty",
trans))
return false;
}
return safe;
}
void Directory::TakeSnapshotForSaveChanges(SaveChangesSnapshot* snapshot) {
ReadTransaction trans(FROM_HERE, this);
ScopedKernelLock lock(this);
if (unrecoverable_error_set(&trans))
return;
for (MetahandleSet::const_iterator i = kernel_->dirty_metahandles.begin();
i != kernel_->dirty_metahandles.end(); ++i) {
EntryKernel* entry = GetEntryByHandle(*i, &lock);
if (!entry)
continue;
if (!entry->is_dirty())
continue;
snapshot->dirty_metas.insert(snapshot->dirty_metas.end(),
new EntryKernel(*entry));
DCHECK_EQ(1U, kernel_->dirty_metahandles.count(*i));
entry->clear_dirty(NULL);
}
ClearDirtyMetahandles();
DCHECK(snapshot->metahandles_to_purge.empty());
snapshot->metahandles_to_purge.swap(kernel_->metahandles_to_purge);
snapshot->kernel_info = kernel_->persisted_info;
snapshot->kernel_info.next_id -= 65536;
snapshot->kernel_info_status = kernel_->info_status;
kernel_->info_status = KERNEL_SHARE_INFO_VALID;
delete_journal_->TakeSnapshotAndClear(
&trans, &snapshot->delete_journals, &snapshot->delete_journals_to_purge);
}
bool Directory::SaveChanges() {
bool success = false;
base::AutoLock scoped_lock(kernel_->save_changes_mutex);
SaveChangesSnapshot snapshot;
TakeSnapshotForSaveChanges(&snapshot);
success = store_->SaveChanges(snapshot);
if (success)
success = VacuumAfterSaveChanges(snapshot);
else
HandleSaveChangesFailure(snapshot);
return success;
}
bool Directory::VacuumAfterSaveChanges(const SaveChangesSnapshot& snapshot) {
if (snapshot.dirty_metas.empty())
return true;
WriteTransaction trans(FROM_HERE, VACUUM_AFTER_SAVE, this);
ScopedKernelLock lock(this);
for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
i != snapshot.dirty_metas.end(); ++i) {
MetahandlesMap::iterator found =
kernel_->metahandles_map.find((*i)->ref(META_HANDLE));
EntryKernel* entry = (found == kernel_->metahandles_map.end() ?
NULL : found->second);
if (entry && SafeToPurgeFromMemory(&trans, entry)) {
size_t num_erased = 0;
num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE));
DCHECK_EQ(1u, num_erased);
num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
DCHECK_EQ(1u, num_erased);
if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
num_erased =
kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG));
DCHECK_EQ(1u, num_erased);
}
if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
num_erased =
kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG));
DCHECK_EQ(1u, num_erased);
}
if (!SyncAssert(!kernel_->parent_child_index.Contains(entry),
FROM_HERE,
"Deleted entry still present",
(&trans)))
return false;
delete entry;
}
if (trans.unrecoverable_error_set())
return false;
}
return true;
}
void Directory::UnapplyEntry(EntryKernel* entry) {
int64 handle = entry->ref(META_HANDLE);
ModelType server_type = GetModelTypeFromSpecifics(
entry->ref(SERVER_SPECIFICS));
if (IsRealDataType(server_type) &&
ModelTypeToRootTag(server_type) == entry->ref(UNIQUE_SERVER_TAG)) {
return;
}
if (IsRealDataType(server_type) && !entry->ref(IS_UNAPPLIED_UPDATE)) {
entry->put(IS_UNAPPLIED_UPDATE, true);
kernel_->unapplied_update_metahandles[server_type].insert(handle);
entry->mark_dirty(&kernel_->dirty_metahandles);
}
if (entry->ref(IS_UNSYNCED)) {
kernel_->unsynced_metahandles.erase(handle);
entry->put(IS_UNSYNCED, false);
entry->mark_dirty(&kernel_->dirty_metahandles);
}
if (!entry->ref(IS_DEL)) {
kernel_->parent_child_index.Remove(entry);
entry->put(IS_DEL, true);
entry->mark_dirty(&kernel_->dirty_metahandles);
}
if (entry->ref(BASE_VERSION) != CHANGES_VERSION) {
entry->put(BASE_VERSION, CHANGES_VERSION);
entry->mark_dirty(&kernel_->dirty_metahandles);
}
}
void Directory::DeleteEntry(bool save_to_journal,
EntryKernel* entry,
EntryKernelSet* entries_to_journal) {
int64 handle = entry->ref(META_HANDLE);
ModelType server_type = GetModelTypeFromSpecifics(
entry->ref(SERVER_SPECIFICS));
kernel_->metahandles_to_purge.insert(handle);
size_t num_erased = 0;
num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE));
DCHECK_EQ(1u, num_erased);
num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
DCHECK_EQ(1u, num_erased);
num_erased = kernel_->unsynced_metahandles.erase(handle);
DCHECK_EQ(entry->ref(IS_UNSYNCED), num_erased > 0);
num_erased =
kernel_->unapplied_update_metahandles[server_type].erase(handle);
DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0);
if (kernel_->parent_child_index.Contains(entry))
kernel_->parent_child_index.Remove(entry);
if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
num_erased =
kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG));
DCHECK_EQ(1u, num_erased);
}
if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
num_erased =
kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG));
DCHECK_EQ(1u, num_erased);
}
if (save_to_journal) {
entries_to_journal->insert(entry);
} else {
delete entry;
}
}
bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet disabled_types,
ModelTypeSet types_to_journal,
ModelTypeSet types_to_unapply) {
disabled_types.RemoveAll(ProxyTypes());
if (disabled_types.Empty())
return true;
{
WriteTransaction trans(FROM_HERE, PURGE_ENTRIES, this);
EntryKernelSet entries_to_journal;
STLElementDeleter<EntryKernelSet> journal_deleter(&entries_to_journal);
{
ScopedKernelLock lock(this);
std::set<EntryKernel*> to_purge;
for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
it != kernel_->metahandles_map.end(); ++it) {
const sync_pb::EntitySpecifics& local_specifics =
it->second->ref(SPECIFICS);
const sync_pb::EntitySpecifics& server_specifics =
it->second->ref(SERVER_SPECIFICS);
ModelType local_type = GetModelTypeFromSpecifics(local_specifics);
ModelType server_type = GetModelTypeFromSpecifics(server_specifics);
if ((IsRealDataType(local_type) && disabled_types.Has(local_type)) ||
(IsRealDataType(server_type) && disabled_types.Has(server_type))) {
to_purge.insert(it->second);
}
}
for (std::set<EntryKernel*>::iterator it = to_purge.begin();
it != to_purge.end(); ++it) {
EntryKernel* entry = *it;
const sync_pb::EntitySpecifics& local_specifics =
(*it)->ref(SPECIFICS);
const sync_pb::EntitySpecifics& server_specifics =
(*it)->ref(SERVER_SPECIFICS);
ModelType local_type = GetModelTypeFromSpecifics(local_specifics);
ModelType server_type = GetModelTypeFromSpecifics(server_specifics);
if (types_to_unapply.Has(local_type) ||
types_to_unapply.Has(server_type)) {
UnapplyEntry(entry);
} else {
bool save_to_journal =
(types_to_journal.Has(local_type) ||
types_to_journal.Has(server_type)) &&
(delete_journal_->IsDeleteJournalEnabled(local_type) ||
delete_journal_->IsDeleteJournalEnabled(server_type));
DeleteEntry(save_to_journal, entry, &entries_to_journal);
}
}
delete_journal_->AddJournalBatch(&trans, entries_to_journal);
for (ModelTypeSet::Iterator it = disabled_types.First();
it.Good(); it.Inc()) {
kernel_->persisted_info.transaction_version[it.Get()] = 0;
if (!types_to_unapply.Has(it.Get()))
kernel_->persisted_info.reset_download_progress(it.Get());
}
}
}
return true;
}
void Directory::HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot) {
WriteTransaction trans(FROM_HERE, HANDLE_SAVE_FAILURE, this);
ScopedKernelLock lock(this);
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
i != snapshot.dirty_metas.end(); ++i) {
MetahandlesMap::iterator found =
kernel_->metahandles_map.find((*i)->ref(META_HANDLE));
if (found != kernel_->metahandles_map.end()) {
found->second->mark_dirty(&kernel_->dirty_metahandles);
}
}
kernel_->metahandles_to_purge.insert(snapshot.metahandles_to_purge.begin(),
snapshot.metahandles_to_purge.end());
delete_journal_->AddJournalBatch(&trans, snapshot.delete_journals);
delete_journal_->PurgeDeleteJournals(&trans,
snapshot.delete_journals_to_purge);
}
void Directory::GetDownloadProgress(
ModelType model_type,
sync_pb::DataTypeProgressMarker* value_out) const {
ScopedKernelLock lock(this);
return value_out->CopyFrom(
kernel_->persisted_info.download_progress[model_type]);
}
void Directory::GetDownloadProgressAsString(
ModelType model_type,
std::string* value_out) const {
ScopedKernelLock lock(this);
kernel_->persisted_info.download_progress[model_type].SerializeToString(
value_out);
}
size_t Directory::GetEntriesCount() const {
ScopedKernelLock lock(this);
return kernel_->metahandles_map.size();
}
void Directory::SetDownloadProgress(
ModelType model_type,
const sync_pb::DataTypeProgressMarker& new_progress) {
ScopedKernelLock lock(this);
kernel_->persisted_info.download_progress[model_type].CopyFrom(new_progress);
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
int64 Directory::GetTransactionVersion(ModelType type) const {
kernel_->transaction_mutex.AssertAcquired();
return kernel_->persisted_info.transaction_version[type];
}
void Directory::IncrementTransactionVersion(ModelType type) {
kernel_->transaction_mutex.AssertAcquired();
kernel_->persisted_info.transaction_version[type]++;
}
void Directory::GetDataTypeContext(BaseTransaction* trans,
ModelType type,
sync_pb::DataTypeContext* context) const {
ScopedKernelLock lock(this);
context->CopyFrom(kernel_->persisted_info.datatype_context[type]);
}
void Directory::SetDataTypeContext(
BaseWriteTransaction* trans,
ModelType type,
const sync_pb::DataTypeContext& context) {
ScopedKernelLock lock(this);
kernel_->persisted_info.datatype_context[type].CopyFrom(context);
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
ModelTypeSet Directory::InitialSyncEndedTypes() {
syncable::ReadTransaction trans(FROM_HERE, this);
ModelTypeSet protocol_types = ProtocolTypes();
ModelTypeSet initial_sync_ended_types;
for (ModelTypeSet::Iterator i = protocol_types.First(); i.Good(); i.Inc()) {
if (InitialSyncEndedForType(&trans, i.Get())) {
initial_sync_ended_types.Put(i.Get());
}
}
return initial_sync_ended_types;
}
bool Directory::InitialSyncEndedForType(ModelType type) {
syncable::ReadTransaction trans(FROM_HERE, this);
return InitialSyncEndedForType(&trans, type);
}
bool Directory::InitialSyncEndedForType(
BaseTransaction* trans, ModelType type) {
syncable::Entry entry(trans,
syncable::GET_BY_SERVER_TAG,
ModelTypeToRootTag(type));
return entry.good() && entry.GetBaseVersion() != CHANGES_VERSION;
}
string Directory::store_birthday() const {
ScopedKernelLock lock(this);
return kernel_->persisted_info.store_birthday;
}
void Directory::set_store_birthday(const string& store_birthday) {
ScopedKernelLock lock(this);
if (kernel_->persisted_info.store_birthday == store_birthday)
return;
kernel_->persisted_info.store_birthday = store_birthday;
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
string Directory::bag_of_chips() const {
ScopedKernelLock lock(this);
return kernel_->persisted_info.bag_of_chips;
}
void Directory::set_bag_of_chips(const string& bag_of_chips) {
ScopedKernelLock lock(this);
if (kernel_->persisted_info.bag_of_chips == bag_of_chips)
return;
kernel_->persisted_info.bag_of_chips = bag_of_chips;
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
string Directory::cache_guid() const {
return kernel_->cache_guid;
}
NigoriHandler* Directory::GetNigoriHandler() {
return nigori_handler_;
}
Cryptographer* Directory::GetCryptographer(const BaseTransaction* trans) {
DCHECK_EQ(this, trans->directory());
return cryptographer_;
}
void Directory::GetAllMetaHandles(BaseTransaction* trans,
MetahandleSet* result) {
result->clear();
ScopedKernelLock lock(this);
for (MetahandlesMap::iterator i = kernel_->metahandles_map.begin();
i != kernel_->metahandles_map.end(); ++i) {
result->insert(i->first);
}
}
void Directory::GetUnsyncedMetaHandles(BaseTransaction* trans,
Metahandles* result) {
result->clear();
ScopedKernelLock lock(this);
copy(kernel_->unsynced_metahandles.begin(),
kernel_->unsynced_metahandles.end(), back_inserter(*result));
}
int64 Directory::unsynced_entity_count() const {
ScopedKernelLock lock(this);
return kernel_->unsynced_metahandles.size();
}
bool Directory::TypeHasUnappliedUpdates(ModelType type) {
ScopedKernelLock lock(this);
return !kernel_->unapplied_update_metahandles[type].empty();
}
void Directory::GetUnappliedUpdateMetaHandles(
BaseTransaction* trans,
FullModelTypeSet server_types,
std::vector<int64>* result) {
result->clear();
ScopedKernelLock lock(this);
for (int i = UNSPECIFIED; i < MODEL_TYPE_COUNT; ++i) {
const ModelType type = ModelTypeFromInt(i);
if (server_types.Has(type)) {
std::copy(kernel_->unapplied_update_metahandles[type].begin(),
kernel_->unapplied_update_metahandles[type].end(),
back_inserter(*result));
}
}
}
void Directory::GetMetaHandlesOfType(BaseTransaction* trans,
ModelType type,
std::vector<int64>* result) {
result->clear();
ScopedKernelLock lock(this);
for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
it != kernel_->metahandles_map.end(); ++it) {
EntryKernel* entry = it->second;
const ModelType entry_type =
GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
if (entry_type == type)
result->push_back(it->first);
}
}
void Directory::CollectMetaHandleCounts(
std::vector<int>* num_entries_by_type,
std::vector<int>* num_to_delete_entries_by_type) {
syncable::ReadTransaction trans(FROM_HERE, this);
ScopedKernelLock lock(this);
for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
it != kernel_->metahandles_map.end(); ++it) {
EntryKernel* entry = it->second;
const ModelType type = GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
(*num_entries_by_type)[type]++;
if (entry->ref(IS_DEL))
(*num_to_delete_entries_by_type)[type]++;
}
}
scoped_ptr<base::ListValue> Directory::GetAllNodeDetails(
BaseTransaction* trans) {
scoped_ptr<base::ListValue> nodes(new base::ListValue());
ScopedKernelLock lock(this);
for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
it != kernel_->metahandles_map.end(); ++it) {
EntryKernel* kernel = it->second;
scoped_ptr<base::DictionaryValue> node(
kernel->ToValue(GetCryptographer(trans)));
if (kernel->ShouldMaintainPosition() && !kernel->ref(IS_DEL)) {
node->SetInteger("positionIndex", GetPositionIndex(trans, kernel));
}
nodes->Append(node.release());
}
return nodes.Pass();
}
bool Directory::CheckInvariantsOnTransactionClose(
syncable::BaseTransaction* trans,
const MetahandleSet& modified_handles) {
switch (invariant_check_level_) {
case FULL_DB_VERIFICATION: {
MetahandleSet all_handles;
GetAllMetaHandles(trans, &all_handles);
return CheckTreeInvariants(trans, all_handles);
}
case VERIFY_CHANGES: {
return CheckTreeInvariants(trans, modified_handles);
}
case OFF: {
return true;
}
}
NOTREACHED();
return false;
}
bool Directory::FullyCheckTreeInvariants(syncable::BaseTransaction* trans) {
MetahandleSet handles;
GetAllMetaHandles(trans, &handles);
return CheckTreeInvariants(trans, handles);
}
bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
const MetahandleSet& handles) {
MetahandleSet::const_iterator i;
for (i = handles.begin() ; i != handles.end() ; ++i) {
int64 metahandle = *i;
Entry e(trans, GET_BY_HANDLE, metahandle);
if (!SyncAssert(e.good(), FROM_HERE, "Entry is bad", trans))
return false;
syncable::Id id = e.GetId();
syncable::Id parentid = e.GetParentId();
if (id.IsRoot()) {
if (!SyncAssert(e.GetIsDir(), FROM_HERE,
"Entry should be a directory",
trans))
return false;
if (!SyncAssert(parentid.IsRoot(), FROM_HERE,
"Entry should be root",
trans))
return false;
if (!SyncAssert(!e.GetIsUnsynced(), FROM_HERE,
"Entry should be sycned",
trans))
return false;
continue;
}
if (!e.GetIsDel()) {
if (!SyncAssert(id != parentid, FROM_HERE,
"Id should be different from parent id.",
trans))
return false;
if (!SyncAssert(!e.GetNonUniqueName().empty(), FROM_HERE,
"Non unique name should not be empty.",
trans))
return false;
int safety_count = handles.size() + 1;
while (!parentid.IsRoot()) {
Entry parent(trans, GET_BY_ID, parentid);
if (!SyncAssert(parent.good(), FROM_HERE,
"Parent entry is not valid.",
trans))
return false;
if (handles.end() == handles.find(parent.GetMetahandle()))
break;
if (!SyncAssert(parent.GetIsDir(), FROM_HERE,
"Parent should be a directory",
trans))
return false;
if (!SyncAssert(!parent.GetIsDel(), FROM_HERE,
"Parent should not have been marked for deletion.",
trans))
return false;
if (!SyncAssert(handles.end() != handles.find(parent.GetMetahandle()),
FROM_HERE,
"Parent should be in the index.",
trans))
return false;
parentid = parent.GetParentId();
if (!SyncAssert(--safety_count > 0, FROM_HERE,
"Count should be greater than zero.",
trans))
return false;
}
}
int64 base_version = e.GetBaseVersion();
int64 server_version = e.GetServerVersion();
bool using_unique_client_tag = !e.GetUniqueClientTag().empty();
if (CHANGES_VERSION == base_version || 0 == base_version) {
if (e.GetIsUnappliedUpdate()) {
if (!using_unique_client_tag) {
if (!SyncAssert(e.GetIsDel(), FROM_HERE,
"The entry should not have been deleted.",
trans))
return false;
}
if (!SyncAssert(id.ServerKnows(), FROM_HERE,
"The id should be from a server.",
trans))
return false;
} else {
if (e.GetIsDir()) {
if (!SyncAssert(!using_unique_client_tag, FROM_HERE,
"Directory cannot have a client tag.",
trans))
return false;
}
if (!e.GetIsDel()) {
if (!SyncAssert(e.GetIsUnsynced(), FROM_HERE,
"The item should be unsynced.",
trans))
return false;
}
if (!SyncAssert(0 == server_version, FROM_HERE,
"Server version should be zero.",
trans))
return false;
if (!using_unique_client_tag) {
if (!SyncAssert(!id.ServerKnows(), FROM_HERE,
"Should be a client only id.",
trans))
return false;
}
}
} else {
if (!SyncAssert(id.ServerKnows(),
FROM_HERE,
"Should be a server id.",
trans))
return false;
}
if (!SyncAssert(!(!id.ServerKnows() && e.GetIsDel() && e.GetIsUnsynced()),
FROM_HERE,
"Locally deleted item must not be unsynced.",
trans)) {
return false;
}
}
return true;
}
void Directory::SetInvariantCheckLevel(InvariantCheckLevel check_level) {
invariant_check_level_ = check_level;
}
int64 Directory::NextMetahandle() {
ScopedKernelLock lock(this);
int64 metahandle = (kernel_->next_metahandle)++;
return metahandle;
}
Id Directory::NextId() {
int64 result;
{
ScopedKernelLock lock(this);
result = (kernel_->persisted_info.next_id)--;
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
DCHECK_LT(result, 0);
return Id::CreateFromClientString(base::Int64ToString(result));
}
bool Directory::HasChildren(BaseTransaction* trans, const Id& id) {
ScopedKernelLock lock(this);
return kernel_->parent_child_index.GetChildren(id) != NULL;
}
Id Directory::GetFirstChildId(BaseTransaction* trans,
const EntryKernel* parent) {
DCHECK(parent);
DCHECK(parent->ref(IS_DIR));
ScopedKernelLock lock(this);
const OrderedChildSet* children =
kernel_->parent_child_index.GetChildren(parent->ref(ID));
if (!children)
return Id();
return (*children->begin())->ref(ID);
}
syncable::Id Directory::GetPredecessorId(EntryKernel* e) {
ScopedKernelLock lock(this);
DCHECK(ParentChildIndex::ShouldInclude(e));
const OrderedChildSet* children =
kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
DCHECK(children && !children->empty());
OrderedChildSet::const_iterator i = children->find(e);
DCHECK(i != children->end());
if (i == children->begin()) {
return Id();
} else {
i--;
return (*i)->ref(ID);
}
}
syncable::Id Directory::GetSuccessorId(EntryKernel* e) {
ScopedKernelLock lock(this);
DCHECK(ParentChildIndex::ShouldInclude(e));
const OrderedChildSet* children =
kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
DCHECK(children && !children->empty());
OrderedChildSet::const_iterator i = children->find(e);
DCHECK(i != children->end());
i++;
if (i == children->end()) {
return Id();
} else {
return (*i)->ref(ID);
}
}
void Directory::PutPredecessor(EntryKernel* e, EntryKernel* predecessor) {
DCHECK(!e->ref(IS_DEL));
if (!e->ShouldMaintainPosition()) {
DCHECK(!e->ref(UNIQUE_POSITION).IsValid());
return;
}
std::string suffix = e->ref(UNIQUE_BOOKMARK_TAG);
DCHECK(!suffix.empty());
ScopedKernelLock lock(this);
ScopedParentChildIndexUpdater updater(lock, e, &kernel_->parent_child_index);
const OrderedChildSet* siblings =
kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
if (!siblings) {
DCHECK(predecessor->ref(ID).IsRoot());
UniquePosition pos = UniquePosition::InitialPosition(suffix);
e->put(UNIQUE_POSITION, pos);
return;
}
if (predecessor->ref(ID).IsRoot()) {
UniquePosition successor_pos = (*siblings->begin())->ref(UNIQUE_POSITION);
UniquePosition pos;
if (!successor_pos.IsValid()) {
pos = UniquePosition::InitialPosition(suffix);
} else {
DCHECK(!siblings->empty());
pos = UniquePosition::Before(successor_pos, suffix);
}
e->put(UNIQUE_POSITION, pos);
return;
}
DCHECK(predecessor->ref(UNIQUE_POSITION).IsValid());
OrderedChildSet::const_iterator neighbour = siblings->find(predecessor);
DCHECK(neighbour != siblings->end());
++neighbour;
if (neighbour == siblings->end()) {
UniquePosition pos = UniquePosition::After(
predecessor->ref(UNIQUE_POSITION),
suffix);
e->put(UNIQUE_POSITION, pos);
return;
}
EntryKernel* successor = *neighbour;
DCHECK(successor->ref(UNIQUE_POSITION).IsValid());
UniquePosition pos = UniquePosition::Between(
predecessor->ref(UNIQUE_POSITION),
successor->ref(UNIQUE_POSITION),
suffix);
e->put(UNIQUE_POSITION, pos);
return;
}
void Directory::AppendChildHandles(const ScopedKernelLock& lock,
const Id& parent_id,
Directory::Metahandles* result) {
const OrderedChildSet* children =
kernel_->parent_child_index.GetChildren(parent_id);
if (!children)
return;
for (OrderedChildSet::const_iterator i = children->begin();
i != children->end(); ++i) {
DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID));
result->push_back((*i)->ref(META_HANDLE));
}
}
}
}