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

Side by Side Diff: sync/syncable/directory_backing_store.cc

Issue 11636006: WIP: The Bookmark Position Megapatch (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Various updates, including switch suffix to unique_client_tag style Created 8 years 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sync/syncable/directory_backing_store.h" 5 #include "sync/syncable/directory_backing_store.h"
6 6
7 #include "build/build_config.h" 7 #include "build/build_config.h"
8 8
9 #include <limits> 9 #include <limits>
10 10
11 #include "base/base64.h" 11 #include "base/base64.h"
12 #include "base/debug/trace_event.h" 12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h" 13 #include "base/file_util.h"
14 #include "base/hash_tables.h" 14 #include "base/hash_tables.h"
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "base/metrics/histogram.h" 16 #include "base/metrics/histogram.h"
17 #include "base/rand_util.h" 17 #include "base/rand_util.h"
18 #include "base/sha1.h"
18 #include "base/stl_util.h" 19 #include "base/stl_util.h"
19 #include "base/string_number_conversions.h" 20 #include "base/string_number_conversions.h"
20 #include "base/stringprintf.h" 21 #include "base/stringprintf.h"
21 #include "base/time.h" 22 #include "base/time.h"
22 #include "sql/connection.h" 23 #include "sql/connection.h"
23 #include "sql/statement.h" 24 #include "sql/statement.h"
24 #include "sql/transaction.h" 25 #include "sql/transaction.h"
25 #include "sync/internal_api/public/base/node_ordinal.h" 26 #include "sync/internal_api/public/base/node_ordinal.h"
26 #include "sync/protocol/bookmark_specifics.pb.h" 27 #include "sync/protocol/bookmark_specifics.pb.h"
27 #include "sync/protocol/sync.pb.h" 28 #include "sync/protocol/sync.pb.h"
28 #include "sync/syncable/syncable-inl.h" 29 #include "sync/syncable/syncable-inl.h"
29 #include "sync/syncable/syncable_columns.h" 30 #include "sync/syncable/syncable_columns.h"
30 #include "sync/util/time.h" 31 #include "sync/util/time.h"
31 32
32 using std::string; 33 using std::string;
33 34
34 namespace syncer { 35 namespace syncer {
35 namespace syncable { 36 namespace syncable {
36 37
37 // This just has to be big enough to hold an UPDATE or INSERT statement that 38 // This just has to be big enough to hold an UPDATE or INSERT statement that
38 // modifies all the columns in the entry table. 39 // modifies all the columns in the entry table.
39 static const string::size_type kUpdateStatementBufferSize = 2048; 40 static const string::size_type kUpdateStatementBufferSize = 2048;
40 41
41 // Increment this version whenever updating DB tables. 42 // Increment this version whenever updating DB tables.
42 extern const int32 kCurrentDBVersion; // Global visibility for our unittest. 43 extern const int32 kCurrentDBVersion; // Global visibility for our unittest.
43 const int32 kCurrentDBVersion = 85; 44 const int32 kCurrentDBVersion = 86;
44 45
45 // Iterate over the fields of |entry| and bind each to |statement| for 46 // Iterate over the fields of |entry| and bind each to |statement| for
46 // updating. Returns the number of args bound. 47 // updating. Returns the number of args bound.
47 void BindFields(const EntryKernel& entry, 48 void BindFields(const EntryKernel& entry,
48 sql::Statement* statement) { 49 sql::Statement* statement) {
49 int index = 0; 50 int index = 0;
50 int i = 0; 51 int i = 0;
51 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) { 52 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
52 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i))); 53 statement->BindInt64(index++, entry.ref(static_cast<Int64Field>(i)));
53 } 54 }
54 for ( ; i < TIME_FIELDS_END; ++i) { 55 for ( ; i < TIME_FIELDS_END; ++i) {
55 statement->BindInt64(index++, 56 statement->BindInt64(index++,
56 TimeToProtoTime( 57 TimeToProtoTime(
57 entry.ref(static_cast<TimeField>(i)))); 58 entry.ref(static_cast<TimeField>(i))));
58 } 59 }
59 for ( ; i < ID_FIELDS_END; ++i) { 60 for ( ; i < ID_FIELDS_END; ++i) {
60 statement->BindString(index++, entry.ref(static_cast<IdField>(i)).s_); 61 statement->BindString(index++, entry.ref(static_cast<IdField>(i)).s_);
61 } 62 }
62 for ( ; i < BIT_FIELDS_END; ++i) { 63 for ( ; i < BIT_FIELDS_END; ++i) {
63 statement->BindInt(index++, entry.ref(static_cast<BitField>(i))); 64 statement->BindInt(index++, entry.ref(static_cast<BitField>(i)));
64 } 65 }
65 for ( ; i < STRING_FIELDS_END; ++i) { 66 for ( ; i < STRING_FIELDS_END; ++i) {
66 statement->BindString(index++, entry.ref(static_cast<StringField>(i))); 67 statement->BindString(index++, entry.ref(static_cast<StringField>(i)));
67 } 68 }
68 std::string temp;
69 for ( ; i < PROTO_FIELDS_END; ++i) { 69 for ( ; i < PROTO_FIELDS_END; ++i) {
70 std::string temp;
70 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp); 71 entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp);
71 statement->BindBlob(index++, temp.data(), temp.length()); 72 statement->BindBlob(index++, temp.data(), temp.length());
72 } 73 }
73 for( ; i < ORDINAL_FIELDS_END; ++i) { 74 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
74 temp = entry.ref(static_cast<OrdinalField>(i)).ToInternalValue(); 75 const std::string& temp =
76 entry.ref(static_cast<UniquePositionField>(i)).ToInternalValue();
77 statement->BindBlob(index++, temp.data(), temp.length());
78 }
79 for ( ; i < BYTES_FIELDS_END; ++i) {
80 const std::string& temp = entry.ref(static_cast<BytesField>(i));
75 statement->BindBlob(index++, temp.data(), temp.length()); 81 statement->BindBlob(index++, temp.data(), temp.length());
76 } 82 }
77 } 83 }
78 84
79 // The caller owns the returned EntryKernel*. Assumes the statement currently 85 // The caller owns the returned EntryKernel*. Assumes the statement currently
80 // points to a valid row in the metas table. Returns NULL to indicate that 86 // points to a valid row in the metas table. Returns NULL to indicate that
81 // it detected a corruption in the data on unpacking. 87 // it detected a corruption in the data on unpacking.
82 scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement) { 88 scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement) {
83 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 89 scoped_ptr<EntryKernel> kernel(new EntryKernel());
84 DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT)); 90 DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT));
(...skipping 13 matching lines...) Expand all
98 kernel->put(static_cast<BitField>(i), (0 != statement->ColumnInt(i))); 104 kernel->put(static_cast<BitField>(i), (0 != statement->ColumnInt(i)));
99 } 105 }
100 for ( ; i < STRING_FIELDS_END; ++i) { 106 for ( ; i < STRING_FIELDS_END; ++i) {
101 kernel->put(static_cast<StringField>(i), 107 kernel->put(static_cast<StringField>(i),
102 statement->ColumnString(i)); 108 statement->ColumnString(i));
103 } 109 }
104 for ( ; i < PROTO_FIELDS_END; ++i) { 110 for ( ; i < PROTO_FIELDS_END; ++i) {
105 kernel->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray( 111 kernel->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray(
106 statement->ColumnBlob(i), statement->ColumnByteLength(i)); 112 statement->ColumnBlob(i), statement->ColumnByteLength(i));
107 } 113 }
108 for( ; i < ORDINAL_FIELDS_END; ++i) { 114 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
109 std::string temp; 115 std::string temp;
110 statement->ColumnBlobAsString(i, &temp); 116 statement->ColumnBlobAsString(i, &temp);
111 NodeOrdinal unpacked_ord(temp);
112 117
113 // Its safe to assume that an invalid ordinal is a sign that 118 // An empty value indicates an intentionally invalid position.
114 // some external corruption has occurred. Return NULL to force 119 // If the string is non-empty, though, the position should be valid.
115 // a re-download of the sync data. 120 if (!temp.empty()) {
116 if(!unpacked_ord.IsValid()) { 121 UniquePosition unpacked_pos = UniquePosition::FromBytes(temp);
117 DVLOG(1) << "Unpacked invalid ordinal. Signaling that the DB is corrupt"; 122 if(!unpacked_pos.IsValid()) {
118 return scoped_ptr<EntryKernel>(NULL); 123 DVLOG(1) << "Unpacked invalid position. Assuming the DB is corrupt";
124 return scoped_ptr<EntryKernel>(NULL);
125 }
126 kernel->mutable_ref(static_cast<UniquePositionField>(i)) = unpacked_pos;
119 } 127 }
120 kernel->mutable_ref(static_cast<OrdinalField>(i)) = unpacked_ord; 128 }
129 for ( ; i < BYTES_FIELDS_END; ++i) {
130 std::string temp;
131 statement->ColumnBlobAsString(i, &temp);
132 kernel->put(static_cast<BytesField>(i), temp);
121 } 133 }
122 return kernel.Pass(); 134 return kernel.Pass();
123 } 135 }
124 136
125 namespace { 137 namespace {
126 138
127 string ComposeCreateTableColumnSpecs() { 139 string ComposeCreateTableColumnSpecs() {
128 const ColumnSpec* begin = g_metas_columns; 140 const ColumnSpec* begin = g_metas_columns;
129 const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns); 141 const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns);
130 string query; 142 string query;
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 if (MigrateVersion83To84()) 382 if (MigrateVersion83To84())
371 version_on_disk = 84; 383 version_on_disk = 84;
372 } 384 }
373 385
374 // Version 85 migration removes the initial_sync_ended bits. 386 // Version 85 migration removes the initial_sync_ended bits.
375 if (version_on_disk == 84) { 387 if (version_on_disk == 84) {
376 if (MigrateVersion84To85()) 388 if (MigrateVersion84To85())
377 version_on_disk = 85; 389 version_on_disk = 85;
378 } 390 }
379 391
392 // Version 86 migration converts bookmarks to the unique positioning system.
393 // It also introduces a new field to store a unique ID for each bookmark.
394 if (version_on_disk == 85) {
395 if (MigrateVersion85To86())
396 version_on_disk = 86;
397 }
398
380 // If one of the migrations requested it, drop columns that aren't current. 399 // If one of the migrations requested it, drop columns that aren't current.
381 // It's only safe to do this after migrating all the way to the current 400 // It's only safe to do this after migrating all the way to the current
382 // version. 401 // version.
383 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) { 402 if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) {
384 if (!RefreshColumns()) 403 if (!RefreshColumns())
385 version_on_disk = 0; 404 version_on_disk = 0;
386 } 405 }
387 406
388 // A final, alternative catch-all migration to simply re-sync everything. 407 // A final, alternative catch-all migration to simply re-sync everything.
389 if (version_on_disk != kCurrentDBVersion) { 408 if (version_on_disk != kCurrentDBVersion) {
(...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after
968 needs_column_refresh_ = true; 987 needs_column_refresh_ = true;
969 return true; 988 return true;
970 } 989 }
971 990
972 bool DirectoryBackingStore::MigrateVersion76To77() { 991 bool DirectoryBackingStore::MigrateVersion76To77() {
973 // This change changes the format of stored timestamps to ms since 992 // This change changes the format of stored timestamps to ms since
974 // the Unix epoch. 993 // the Unix epoch.
975 #if defined(OS_WIN) 994 #if defined(OS_WIN)
976 // On Windows, we used to store timestamps in FILETIME format (100s of 995 // On Windows, we used to store timestamps in FILETIME format (100s of
977 // ns since Jan 1, 1601). Magic numbers taken from 996 // ns since Jan 1, 1601). Magic numbers taken from
978 // http://stackoverflow.com/questions/5398557/java-library-for-dealing-with-win3 2-filetime 997 // http://stackoverflow.com/questions/5398557/
998 // java-library-for-dealing-with-win32-filetime
979 // . 999 // .
980 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000" 1000 #define TO_UNIX_TIME_MS(x) #x " = " #x " / 10000 - 11644473600000"
981 #else 1001 #else
982 // On other platforms, we used to store timestamps in time_t format (s 1002 // On other platforms, we used to store timestamps in time_t format (s
983 // since the Unix epoch). 1003 // since the Unix epoch).
984 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000" 1004 #define TO_UNIX_TIME_MS(x) #x " = " #x " * 1000"
985 #endif 1005 #endif
986 sql::Statement update_timestamps(db_->GetUniqueStatement( 1006 sql::Statement update_timestamps(db_->GetUniqueStatement(
987 "UPDATE metas SET " 1007 "UPDATE metas SET "
988 TO_UNIX_TIME_MS(mtime) ", " 1008 TO_UNIX_TIME_MS(mtime) ", "
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
1093 string query = "CREATE TABLE deleted_metas "; 1113 string query = "CREATE TABLE deleted_metas ";
1094 query.append(ComposeCreateTableColumnSpecs()); 1114 query.append(ComposeCreateTableColumnSpecs());
1095 if (!db_->Execute(query.c_str())) 1115 if (!db_->Execute(query.c_str()))
1096 return false; 1116 return false;
1097 1117
1098 SetVersion(84); 1118 SetVersion(84);
1099 return true; 1119 return true;
1100 } 1120 }
1101 1121
1102 bool DirectoryBackingStore::MigrateVersion84To85() { 1122 bool DirectoryBackingStore::MigrateVersion84To85() {
1103 // Version 84 removes the initial_sync_ended flag. 1123 // Version 85 removes the initial_sync_ended flag.
1104 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models")) 1124 if (!db_->Execute("ALTER TABLE models RENAME TO temp_models"))
1105 return false; 1125 return false;
1106 if (!CreateModelsTable()) 1126 if (!CreateModelsTable())
1107 return false; 1127 return false;
1108 if (!db_->Execute("INSERT INTO models SELECT " 1128 if (!db_->Execute("INSERT INTO models SELECT "
1109 "model_id, progress_marker, transaction_version " 1129 "model_id, progress_marker, transaction_version "
1110 "FROM temp_models")) { 1130 "FROM temp_models")) {
1111 return false; 1131 return false;
1112 } 1132 }
1113 SafeDropTable("temp_models"); 1133 SafeDropTable("temp_models");
1114 1134
1115 SetVersion(85); 1135 SetVersion(85);
1116 return true; 1136 return true;
1117 } 1137 }
1118 1138
1139 bool DirectoryBackingStore::MigrateVersion85To86() {
1140 // Version 86 removes both server ordinals and local NEXT_ID, PREV_ID and
1141 // SERVER_{POSITION,ORDINAL}_IN_PARENT and replaces them with UNIQUE_POSITION
1142 // and SERVER_UNIQUE_POSITION.
1143 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1144 "server_unique_position BLOB")) {
1145 return false;
1146 }
1147 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1148 "unique_position BLOB")) {
1149 return false;
1150 }
1151 if (!db_->Execute("ALTER TABLE metas ADD COLUMN "
1152 "unique_bookmark_tag BLOB")) {
1153 return false;
1154 }
1155
1156 sql::Statement get(db_->GetUniqueStatement(
1157 "SELECT metahandle, id, specifics, server_ordinal_in_parent FROM metas"));
1158
1159 // Note that we set both the local and server position based on the server
1160 // position. We wll lose any unsynced local position changes. Unfortunately,
1161 // there's nothing we can do to avoid that. The NEXT_ID / PREV_ID values
1162 // can't be translated into a UNIQUE_POSTION in a reliable way.
1163 sql::Statement put(db_->GetCachedStatement(
1164 SQL_FROM_HERE,
1165 "UPDATE metas SET"
1166 " server_unique_position = ?,"
1167 " unique_position = ?,"
1168 " unique_bookmark_tag = ?"
1169 "WHERE metahandle = ?"));
1170
1171 while (get.Step()) {
1172 int64 metahandle = get.ColumnInt64(0);
1173
1174 sync_pb::EntitySpecifics specifics;
1175 specifics.ParseFromArray(
1176 get.ColumnBlob(1), get.ColumnByteLength(1));
1177
1178 std::string id_string;
1179 get.ColumnBlobAsString(2, &id_string);
1180
1181 std::string ordinal_string;
1182 get.ColumnBlobAsString(3, &ordinal_string);
1183 NodeOrdinal ordinal(ordinal_string);
1184
1185 std::string unique_bookmark_tag;
1186
1187 UniquePosition position;
1188 if (ShouldMaintainPosition(GetModelTypeFromSpecifics(specifics))) {
1189 // Ideally, the tag would be based on the originator_cache_guid and
1190 // the originator_item_id. That's something that all clients can
1191 // agree on.
1192 //
1193 // Unfortunately, that information isn't available here. We will base our
1194 // tag on the ID instead, which is slightly less consistent. For fully
1195 // synced items, the tags will match, but there could be some mismatches
1196 // if there happen to be client IDs during the migration.
1197 //
1198 // To get everyone back into a synced state, we will update the bookmark
1199 // tag according to the originator_cache_guid and originator_item_id when
1200 // we see updates for this item. That should ensure that commonly
1201 // modified items will end up with the proper tag values eventually.
1202 std::string unique_bookmark_tag;
1203
1204 // FIXME: Include some sort of tag prefix here to avoid "collisions"?
1205 std::string hash = base::SHA1HashString(id_string);
1206
1207 CHECK(base::Base64Encode(hash, &unique_bookmark_tag));
1208
1209 int64 int_position = NodeOrdinalToInt64(ordinal);
1210 position = UniquePosition::FromInt64(int_position, unique_bookmark_tag);
1211 } else {
1212 // Leave bookmark_tag and position at their default (invalid) values.
1213 }
1214
1215 const std::string position_blob = position.ToInternalValue();
1216 put.BindBlob(0, position_blob.data(), position_blob.length());
1217 put.BindBlob(1, position_blob.data(), position_blob.length());
1218 put.BindBlob(2, unique_bookmark_tag.data(), unique_bookmark_tag.length());
1219 put.BindInt64(3, metahandle);
1220
1221 if (!put.Run())
1222 return false;
1223 put.Reset(true);
1224 }
1225
1226 SetVersion(86);
1227 needs_column_refresh_ = true;
1228 return true;
1229 }
1230
1119 bool DirectoryBackingStore::CreateTables() { 1231 bool DirectoryBackingStore::CreateTables() {
1120 DVLOG(1) << "First run, creating tables"; 1232 DVLOG(1) << "First run, creating tables";
1121 // Create two little tables share_version and share_info 1233 // Create two little tables share_version and share_info
1122 if (!db_->Execute( 1234 if (!db_->Execute(
1123 "CREATE TABLE share_version (" 1235 "CREATE TABLE share_version ("
1124 "id VARCHAR(128) primary key, data INT)")) { 1236 "id VARCHAR(128) primary key, data INT)")) {
1125 return false; 1237 return false;
1126 } 1238 }
1127 1239
1128 { 1240 {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
1171 1283
1172 // Create the big metas table. 1284 // Create the big metas table.
1173 if (!CreateMetasTable(false)) 1285 if (!CreateMetasTable(false))
1174 return false; 1286 return false;
1175 1287
1176 { 1288 {
1177 // Insert the entry for the root into the metas table. 1289 // Insert the entry for the root into the metas table.
1178 const int64 now = TimeToProtoTime(base::Time::Now()); 1290 const int64 now = TimeToProtoTime(base::Time::Now());
1179 sql::Statement s(db_->GetUniqueStatement( 1291 sql::Statement s(db_->GetUniqueStatement(
1180 "INSERT INTO metas " 1292 "INSERT INTO metas "
1181 "( id, metahandle, is_dir, ctime, mtime, server_ordinal_in_parent) " 1293 "( id, metahandle, is_dir, ctime, mtime ) "
1182 "VALUES ( \"r\", 1, 1, ?, ?, ?)")); 1294 "VALUES ( \"r\", 1, 1, ?, ? )"));
1183 s.BindInt64(0, now); 1295 s.BindInt64(0, now);
1184 s.BindInt64(1, now); 1296 s.BindInt64(1, now);
1185 const std::string ord =
1186 NodeOrdinal::CreateInitialOrdinal().ToInternalValue();
1187 s.BindBlob(2, ord.data(), ord.length());
1188 1297
1189 if (!s.Run()) 1298 if (!s.Run())
1190 return false; 1299 return false;
1191 } 1300 }
1192 1301
1193 return true; 1302 return true;
1194 } 1303 }
1195 1304
1196 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) { 1305 bool DirectoryBackingStore::CreateMetasTable(bool is_temporary) {
1197 string query = "CREATE TABLE "; 1306 string query = "CREATE TABLE ";
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
1300 it != index.end(); ++it) { 1409 it != index.end(); ++it) {
1301 EntryKernel* entry = *it; 1410 EntryKernel* entry = *it;
1302 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second); 1411 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second);
1303 is_ok = is_ok && !is_duplicate_id; 1412 is_ok = is_ok && !is_duplicate_id;
1304 } 1413 }
1305 1414
1306 IdsSet::iterator end = ids_set.end(); 1415 IdsSet::iterator end = ids_set.end();
1307 for (MetahandlesIndex::const_iterator it = index.begin(); 1416 for (MetahandlesIndex::const_iterator it = index.begin();
1308 it != index.end(); ++it) { 1417 it != index.end(); ++it) {
1309 EntryKernel* entry = *it; 1418 EntryKernel* entry = *it;
1310 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end);
1311 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end); 1419 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end);
1312 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end); 1420 if (!parent_exists) {
1313 is_ok = is_ok && prev_exists && parent_exists && next_exists; 1421 return false;
1422 }
1314 } 1423 }
1315 return is_ok; 1424 return is_ok;
1316 } 1425 }
1317 1426
1318 } // namespace syncable 1427 } // namespace syncable
1319 } // namespace syncer 1428 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/syncable/directory_backing_store.h ('k') | sync/syncable/directory_backing_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698