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

Side by Side Diff: content/browser/dom_storage/local_storage_context_mojo.cc

Issue 2625873004: Delete and try to recreate localstorage database on invalid schema version. (Closed)
Patch Set: 80 cols, and fix typo Created 3 years, 11 months 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "content/browser/dom_storage/local_storage_context_mojo.h" 5 #include "content/browser/dom_storage/local_storage_context_mojo.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/string_number_conversions.h"
9 #include "components/leveldb/public/cpp/util.h" 9 #include "components/leveldb/public/cpp/util.h"
10 #include "components/leveldb/public/interfaces/leveldb.mojom.h" 10 #include "components/leveldb/public/interfaces/leveldb.mojom.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) { 43 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) {
44 auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize()); 44 auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize());
45 std::vector<uint8_t> key; 45 std::vector<uint8_t> key;
46 key.reserve(arraysize(kMetaPrefix) + serialized_origin.size()); 46 key.reserve(arraysize(kMetaPrefix) + serialized_origin.size());
47 key.insert(key.end(), kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix)); 47 key.insert(key.end(), kMetaPrefix, kMetaPrefix + arraysize(kMetaPrefix));
48 key.insert(key.end(), serialized_origin.begin(), serialized_origin.end()); 48 key.insert(key.end(), serialized_origin.begin(), serialized_origin.end());
49 return key; 49 return key;
50 } 50 }
51 51
52 void NoOpSuccess(bool success) {} 52 void NoOpSuccess(bool success) {}
53 } 53
54 } // namespace
54 55
55 LocalStorageContextMojo::LocalStorageContextMojo( 56 LocalStorageContextMojo::LocalStorageContextMojo(
56 service_manager::Connector* connector, 57 service_manager::Connector* connector,
57 const base::FilePath& subdirectory) 58 const base::FilePath& subdirectory)
58 : connector_(connector), 59 : connector_(connector),
59 subdirectory_(subdirectory), 60 subdirectory_(subdirectory),
60 weak_ptr_factory_(this) {} 61 weak_ptr_factory_(this) {}
61 62
62 LocalStorageContextMojo::~LocalStorageContextMojo() {} 63 LocalStorageContextMojo::~LocalStorageContextMojo() {}
63 64
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 GetStorageUsage(base::BindOnce( 96 GetStorageUsage(base::BindOnce(
96 &LocalStorageContextMojo::OnGotStorageUsageForDeletePhysicalOrigin, 97 &LocalStorageContextMojo::OnGotStorageUsageForDeletePhysicalOrigin,
97 weak_ptr_factory_.GetWeakPtr(), origin)); 98 weak_ptr_factory_.GetWeakPtr(), origin));
98 } 99 }
99 100
100 void LocalStorageContextMojo::Flush() { 101 void LocalStorageContextMojo::Flush() {
101 for (const auto& it : level_db_wrappers_) 102 for (const auto& it : level_db_wrappers_)
102 it.second->ScheduleImmediateCommit(); 103 it.second->ScheduleImmediateCommit();
103 } 104 }
104 105
105 void LocalStorageContextMojo::SetDatabaseForTesting( 106 leveldb::mojom::LevelDBDatabaseAssociatedRequest
106 leveldb::mojom::LevelDBDatabasePtr database) { 107 LocalStorageContextMojo::DatabaseRequestForTesting() {
107 DCHECK_EQ(connection_state_, NO_CONNECTION); 108 DCHECK_EQ(connection_state_, NO_CONNECTION);
108 connection_state_ = CONNECTION_IN_PROGRESS; 109 connection_state_ = CONNECTION_IN_PROGRESS;
109 database_ = std::move(database); 110 leveldb::mojom::LevelDBDatabaseAssociatedRequest request =
111 MakeRequestForTesting(&database_);
110 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); 112 OnDatabaseOpened(leveldb::mojom::DatabaseError::OK);
113 return request;
111 } 114 }
112 115
113 void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { 116 void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) {
114 // If we don't have a filesystem_connection_, we'll need to establish one. 117 // If we don't have a filesystem_connection_, we'll need to establish one.
115 if (connection_state_ == NO_CONNECTION) { 118 if (connection_state_ == NO_CONNECTION) {
116 CHECK(connector_); 119 CHECK(connector_);
117 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); 120 file_service_connection_ = connector_->Connect(file::mojom::kServiceName);
118 connection_state_ = CONNECTION_IN_PROGRESS; 121 connection_state_ = CONNECTION_IN_PROGRESS;
119 file_service_connection_->AddConnectionCompletedClosure( 122 file_service_connection_->AddConnectionCompletedClosure(
120 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, 123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete,
121 weak_ptr_factory_.GetWeakPtr())); 124 weak_ptr_factory_.GetWeakPtr()));
122 file_service_connection_->SetConnectionLostClosure( 125 file_service_connection_->SetConnectionLostClosure(
123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, 126 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError,
124 weak_ptr_factory_.GetWeakPtr())); 127 weak_ptr_factory_.GetWeakPtr()));
125 128
126 if (!subdirectory_.empty()) { 129 InitiateConnection();
127 // We were given a subdirectory to write to. Get it and use a disk backed
128 // database.
129 file_service_connection_->GetInterface(&file_system_);
130 file_system_->GetSubDirectory(
131 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_),
132 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened,
133 weak_ptr_factory_.GetWeakPtr()));
134 } else {
135 // We were not given a subdirectory. Use a memory backed database.
136 file_service_connection_->GetInterface(&leveldb_service_);
137 leveldb_service_->OpenInMemory(
138 MakeRequest(&database_),
139 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
140 weak_ptr_factory_.GetWeakPtr()));
141 }
142 } 130 }
143 131
144 if (connection_state_ == CONNECTION_IN_PROGRESS) { 132 if (connection_state_ == CONNECTION_IN_PROGRESS) {
145 // Queue this OpenLocalStorage call for when we have a level db pointer. 133 // Queue this OpenLocalStorage call for when we have a level db pointer.
146 on_database_opened_callbacks_.push_back(std::move(callback)); 134 on_database_opened_callbacks_.push_back(std::move(callback));
147 return; 135 return;
148 } 136 }
149 137
150 std::move(callback).Run(); 138 std::move(callback).Run();
151 } 139 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 185
198 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { 186 void LocalStorageContextMojo::OnUserServiceConnectionComplete() {
199 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, 187 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED,
200 file_service_connection_->GetResult()); 188 file_service_connection_->GetResult());
201 } 189 }
202 190
203 void LocalStorageContextMojo::OnUserServiceConnectionError() { 191 void LocalStorageContextMojo::OnUserServiceConnectionError() {
204 CHECK(false); 192 CHECK(false);
205 } 193 }
206 194
207 // Part of our asynchronous directory opening called from OpenLocalStorage(). 195 void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) {
196 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
197 if (!subdirectory_.empty() && !in_memory_only) {
198 // We were given a subdirectory to write to. Get it and use a disk backed
199 // database.
200 file_service_connection_->GetInterface(&file_system_);
201 file_system_->GetSubDirectory(
202 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_),
203 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened,
204 weak_ptr_factory_.GetWeakPtr()));
205 } else {
206 // We were not given a subdirectory. Use a memory backed database.
207 file_service_connection_->GetInterface(&leveldb_service_);
208 leveldb_service_->OpenInMemory(
209 MakeRequest(&database_, leveldb_service_.associated_group()),
210 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
211 weak_ptr_factory_.GetWeakPtr()));
212 }
213 }
214
208 void LocalStorageContextMojo::OnDirectoryOpened( 215 void LocalStorageContextMojo::OnDirectoryOpened(
209 filesystem::mojom::FileError err) { 216 filesystem::mojom::FileError err) {
210 if (err != filesystem::mojom::FileError::OK) { 217 if (err != filesystem::mojom::FileError::OK) {
211 // We failed to open the directory; continue with startup so that we create 218 // We failed to open the directory; continue with startup so that we create
212 // the |level_db_wrappers_|. 219 // the |level_db_wrappers_|.
213 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); 220 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR);
214 return; 221 return;
215 } 222 }
216 223
217 // Now that we have a directory, connect to the LevelDB service and get our 224 // Now that we have a directory, connect to the LevelDB service and get our
218 // database. 225 // database.
219 file_service_connection_->GetInterface(&leveldb_service_); 226 file_service_connection_->GetInterface(&leveldb_service_);
220 227
228 // We might still need to use the directory, so create a clone.
229 filesystem::mojom::DirectoryPtr directory_clone;
230 directory_->Clone(MakeRequest(&directory_clone));
231
221 auto options = leveldb::mojom::OpenOptions::New(); 232 auto options = leveldb::mojom::OpenOptions::New();
222 options->create_if_missing = true; 233 options->create_if_missing = true;
223 leveldb_service_->OpenWithOptions( 234 leveldb_service_->OpenWithOptions(
224 std::move(options), std::move(directory_), "leveldb", 235 std::move(options), std::move(directory_clone), "leveldb",
225 MakeRequest(&database_), 236 MakeRequest(&database_, leveldb_service_.associated_group()),
226 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, 237 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
227 weak_ptr_factory_.GetWeakPtr())); 238 weak_ptr_factory_.GetWeakPtr()));
228 } 239 }
229 240
230 void LocalStorageContextMojo::OnDatabaseOpened( 241 void LocalStorageContextMojo::OnDatabaseOpened(
231 leveldb::mojom::DatabaseError status) { 242 leveldb::mojom::DatabaseError status) {
232 if (status != leveldb::mojom::DatabaseError::OK) { 243 if (status != leveldb::mojom::DatabaseError::OK) {
233 // If we failed to open the database, reset the service object so we pass 244 // If we failed to open the database, reset the service object so we pass
234 // null pointers to our wrappers. 245 // null pointers to our wrappers.
235 database_.reset(); 246 database_.reset();
236 leveldb_service_.reset();
237 } 247 }
238 248
239 // We no longer need the file service; we've either transferred |directory_|
240 // to the leveldb service, or we got a file error and no more is possible.
241 directory_.reset();
242 file_system_.reset();
243
244 // Verify DB schema version. 249 // Verify DB schema version.
245 if (database_) { 250 if (database_) {
246 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), 251 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey),
247 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, 252 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion,
248 weak_ptr_factory_.GetWeakPtr())); 253 weak_ptr_factory_.GetWeakPtr()));
249 return; 254 return;
250 } 255 }
251 256
252 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, 257 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR,
253 std::vector<uint8_t>()); 258 std::vector<uint8_t>());
254 } 259 }
255 260
256 void LocalStorageContextMojo::OnGotDatabaseVersion( 261 void LocalStorageContextMojo::OnGotDatabaseVersion(
257 leveldb::mojom::DatabaseError status, 262 leveldb::mojom::DatabaseError status,
258 const std::vector<uint8_t>& value) { 263 const std::vector<uint8_t>& value) {
259 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { 264 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
260 // New database, nothing more to do. Current version will get written 265 // New database, nothing more to do. Current version will get written
261 // when first data is committed. 266 // when first data is committed.
262 } else if (status == leveldb::mojom::DatabaseError::OK) { 267 } else if (status == leveldb::mojom::DatabaseError::OK) {
263 // Existing database, check if version number matches current schema 268 // Existing database, check if version number matches current schema
264 // version. 269 // version.
265 int64_t db_version; 270 int64_t db_version;
266 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), 271 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value),
267 &db_version) || 272 &db_version) ||
268 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { 273 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) {
269 // TODO(mek): delete and recreate database, rather than failing outright. 274 DeleteAndRecreateDatabase();
270 database_ = nullptr; 275 return;
271 } 276 }
272 277
273 database_initialized_ = true; 278 database_initialized_ = true;
274 } else { 279 } else {
275 // Other read error. Possibly database corruption. 280 // Other read error. Possibly database corruption.
276 // TODO(mek): delete and recreate database, rather than failing outright. 281 DeleteAndRecreateDatabase();
277 database_ = nullptr; 282 return;
278 } 283 }
279 284
285 OnConnectionFinished();
286 }
287
288 void LocalStorageContextMojo::OnConnectionFinished() {
289 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
290
291 // We no longer need the file service; we've either transferred |directory_|
292 // to the leveldb service, or we got a file error and no more is possible.
293 directory_.reset();
294 file_system_.reset();
295 if (!database_)
296 leveldb_service_.reset();
297
280 // |database_| should be known to either be valid or invalid by now. Run our 298 // |database_| should be known to either be valid or invalid by now. Run our
281 // delayed bindings. 299 // delayed bindings.
282 connection_state_ = CONNECTION_FINISHED; 300 connection_state_ = CONNECTION_FINISHED;
283 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) 301 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i)
284 std::move(on_database_opened_callbacks_[i]).Run(); 302 std::move(on_database_opened_callbacks_[i]).Run();
285 on_database_opened_callbacks_.clear(); 303 on_database_opened_callbacks_.clear();
286 } 304 }
287 305
306 void LocalStorageContextMojo::DeleteAndRecreateDatabase() {
307 // For now don't support deletion and recreation when already connected.
308 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
309
310 bool recreate_in_memory = false;
311
312 // If tried to recreate database on disk already, try again but this time
313 // in memory.
314 if (tried_to_recreate_ && !subdirectory_.empty()) {
315 recreate_in_memory = true;
316 } else if (tried_to_recreate_) {
317 // Give up completely, run without any database.
318 database_ = nullptr;
319 OnConnectionFinished();
320 return;
321 }
322
323 tried_to_recreate_ = true;
324
325 // Unit tests might not have a file_service_connection_, in which case there
326 // is nothing to retry.
327 if (!file_service_connection_) {
328 database_ = nullptr;
329 OnConnectionFinished();
330 return;
331 }
332
333 // Close and destroy database, and try again.
334 database_ = nullptr;
335 if (directory_.is_bound()) {
336 leveldb_service_->Destroy(
337 std::move(directory_), "leveldb",
338 base::Bind(&LocalStorageContextMojo::OnDBDestroyed,
339 weak_ptr_factory_.GetWeakPtr(), recreate_in_memory));
340 } else {
341 // No directory, so nothing to destroy. Retrying to recreate will probably
342 // fail, but try anyway.
343 InitiateConnection(recreate_in_memory);
344 }
345 }
346
347 void LocalStorageContextMojo::OnDBDestroyed(
348 bool recreate_in_memory,
349 leveldb::mojom::DatabaseError status) {
350 // We're essentially ignoring the status here. Even if destroying failed we
351 // still want to go ahead and try to recreate.
352 InitiateConnection(recreate_in_memory);
353 }
354
288 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called 355 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called
289 // directly from that function, or through |on_database_open_callbacks_|. 356 // directly from that function, or through |on_database_open_callbacks_|.
290 void LocalStorageContextMojo::BindLocalStorage( 357 void LocalStorageContextMojo::BindLocalStorage(
291 const url::Origin& origin, 358 const url::Origin& origin,
292 mojom::LevelDBWrapperRequest request) { 359 mojom::LevelDBWrapperRequest request) {
293 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); 360 GetOrCreateDBWrapper(origin)->Bind(std::move(request));
294 } 361 }
295 362
296 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( 363 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper(
297 const url::Origin& origin) { 364 const url::Origin& origin) {
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 for (const auto& info : usage) { 431 for (const auto& info : usage) {
365 url::Origin origin_candidate(info.origin); 432 url::Origin origin_candidate(info.origin);
366 if (!origin_candidate.IsSameOriginWith(origin) && 433 if (!origin_candidate.IsSameOriginWith(origin) &&
367 origin_candidate.IsSamePhysicalOriginWith(origin)) 434 origin_candidate.IsSamePhysicalOriginWith(origin))
368 DeleteStorage(origin_candidate); 435 DeleteStorage(origin_candidate);
369 } 436 }
370 DeleteStorage(origin); 437 DeleteStorage(origin);
371 } 438 }
372 439
373 } // namespace content 440 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698