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

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: 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 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 CHECK(connector_); 117 CHECK(connector_);
117 file_service_connection_ = connector_->Connect(file::mojom::kServiceName); 118 file_service_connection_ = connector_->Connect(file::mojom::kServiceName);
118 connection_state_ = CONNECTION_IN_PROGRESS; 119 connection_state_ = CONNECTION_IN_PROGRESS;
119 file_service_connection_->AddConnectionCompletedClosure( 120 file_service_connection_->AddConnectionCompletedClosure(
120 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, 121 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete,
121 weak_ptr_factory_.GetWeakPtr())); 122 weak_ptr_factory_.GetWeakPtr()));
122 file_service_connection_->SetConnectionLostClosure( 123 file_service_connection_->SetConnectionLostClosure(
123 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, 124 base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError,
124 weak_ptr_factory_.GetWeakPtr())); 125 weak_ptr_factory_.GetWeakPtr()));
125 126
126 if (!subdirectory_.empty()) { 127 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 } 128 }
143 129
144 if (connection_state_ == CONNECTION_IN_PROGRESS) { 130 if (connection_state_ == CONNECTION_IN_PROGRESS) {
145 // Queue this OpenLocalStorage call for when we have a level db pointer. 131 // Queue this OpenLocalStorage call for when we have a level db pointer.
146 on_database_opened_callbacks_.push_back(std::move(callback)); 132 on_database_opened_callbacks_.push_back(std::move(callback));
147 return; 133 return;
148 } 134 }
149 135
150 std::move(callback).Run(); 136 std::move(callback).Run();
151 } 137 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 183
198 void LocalStorageContextMojo::OnUserServiceConnectionComplete() { 184 void LocalStorageContextMojo::OnUserServiceConnectionComplete() {
199 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, 185 CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED,
200 file_service_connection_->GetResult()); 186 file_service_connection_->GetResult());
201 } 187 }
202 188
203 void LocalStorageContextMojo::OnUserServiceConnectionError() { 189 void LocalStorageContextMojo::OnUserServiceConnectionError() {
204 CHECK(false); 190 CHECK(false);
205 } 191 }
206 192
207 // Part of our asynchronous directory opening called from OpenLocalStorage(). 193 void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) {
194 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
195 if (!subdirectory_.empty() && !in_memory_only) {
196 // We were given a subdirectory to write to. Get it and use a disk backed
197 // database.
198 file_service_connection_->GetInterface(&file_system_);
199 file_system_->GetSubDirectory(
200 subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_),
201 base::Bind(&LocalStorageContextMojo::OnDirectoryOpened,
202 weak_ptr_factory_.GetWeakPtr()));
203 } else {
204 // We were not given a subdirectory. Use a memory backed database.
205 file_service_connection_->GetInterface(&leveldb_service_);
206 leveldb_service_->OpenInMemory(
207 MakeRequest(&database_),
208 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
209 weak_ptr_factory_.GetWeakPtr()));
210 }
211 }
212
208 void LocalStorageContextMojo::OnDirectoryOpened( 213 void LocalStorageContextMojo::OnDirectoryOpened(
209 filesystem::mojom::FileError err) { 214 filesystem::mojom::FileError err) {
210 if (err != filesystem::mojom::FileError::OK) { 215 if (err != filesystem::mojom::FileError::OK) {
211 // We failed to open the directory; continue with startup so that we create 216 // We failed to open the directory; continue with startup so that we create
212 // the |level_db_wrappers_|. 217 // the |level_db_wrappers_|.
213 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); 218 OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR);
214 return; 219 return;
215 } 220 }
216 221
217 // Now that we have a directory, connect to the LevelDB service and get our 222 // Now that we have a directory, connect to the LevelDB service and get our
218 // database. 223 // database.
219 file_service_connection_->GetInterface(&leveldb_service_); 224 file_service_connection_->GetInterface(&leveldb_service_);
220 225
226 // We might still need to directory, so create a clone.
227 filesystem::mojom::DirectoryPtr directory_clone;
228 directory_->Clone(MakeRequest(&directory_clone));
229
221 auto options = leveldb::mojom::OpenOptions::New(); 230 auto options = leveldb::mojom::OpenOptions::New();
222 options->create_if_missing = true; 231 options->create_if_missing = true;
223 leveldb_service_->OpenWithOptions( 232 leveldb_service_->OpenWithOptions(
224 std::move(options), std::move(directory_), "leveldb", 233 std::move(options), std::move(directory_clone), "leveldb",
225 MakeRequest(&database_), 234 MakeRequest(&database_),
226 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, 235 base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
227 weak_ptr_factory_.GetWeakPtr())); 236 weak_ptr_factory_.GetWeakPtr()));
228 } 237 }
229 238
230 void LocalStorageContextMojo::OnDatabaseOpened( 239 void LocalStorageContextMojo::OnDatabaseOpened(
231 leveldb::mojom::DatabaseError status) { 240 leveldb::mojom::DatabaseError status) {
232 if (status != leveldb::mojom::DatabaseError::OK) { 241 if (status != leveldb::mojom::DatabaseError::OK) {
233 // If we failed to open the database, reset the service object so we pass 242 // If we failed to open the database, reset the service object so we pass
234 // null pointers to our wrappers. 243 // null pointers to our wrappers.
235 database_.reset(); 244 database_.reset();
236 leveldb_service_.reset();
237 } 245 }
238 246
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. 247 // Verify DB schema version.
245 if (database_) { 248 if (database_) {
246 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), 249 database_->Get(leveldb::StdStringToUint8Vector(kVersionKey),
247 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, 250 base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion,
248 weak_ptr_factory_.GetWeakPtr())); 251 weak_ptr_factory_.GetWeakPtr()));
249 return; 252 return;
250 } 253 }
251 254
252 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, 255 OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR,
253 std::vector<uint8_t>()); 256 std::vector<uint8_t>());
254 } 257 }
255 258
256 void LocalStorageContextMojo::OnGotDatabaseVersion( 259 void LocalStorageContextMojo::OnGotDatabaseVersion(
257 leveldb::mojom::DatabaseError status, 260 leveldb::mojom::DatabaseError status,
258 const std::vector<uint8_t>& value) { 261 const std::vector<uint8_t>& value) {
259 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { 262 if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
260 // New database, nothing more to do. Current version will get written 263 // New database, nothing more to do. Current version will get written
261 // when first data is committed. 264 // when first data is committed.
262 } else if (status == leveldb::mojom::DatabaseError::OK) { 265 } else if (status == leveldb::mojom::DatabaseError::OK) {
263 // Existing database, check if version number matches current schema 266 // Existing database, check if version number matches current schema
264 // version. 267 // version.
265 int64_t db_version; 268 int64_t db_version;
266 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), 269 if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value),
267 &db_version) || 270 &db_version) ||
268 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { 271 db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) {
269 // TODO(mek): delete and recreate database, rather than failing outright. 272 DeleteAndRecreateDatabase();
270 database_ = nullptr; 273 return;
271 } 274 }
272 275
273 database_initialized_ = true; 276 database_initialized_ = true;
274 } else { 277 } else {
275 // Other read error. Possibly database corruption. 278 // Other read error. Possibly database corruption.
276 // TODO(mek): delete and recreate database, rather than failing outright. 279 DeleteAndRecreateDatabase();
277 database_ = nullptr; 280 return;
278 } 281 }
279 282
283 OnConnectionFinished();
284 }
285
286 void LocalStorageContextMojo::OnConnectionFinished() {
287 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
288
289 // We no longer need the file service; we've either transferred |directory_|
290 // to the leveldb service, or we got a file error and no more is possible.
291 directory_.reset();
292 file_system_.reset();
293 if (!database_)
294 leveldb_service_.reset();
295
280 // |database_| should be known to either be valid or invalid by now. Run our 296 // |database_| should be known to either be valid or invalid by now. Run our
281 // delayed bindings. 297 // delayed bindings.
282 connection_state_ = CONNECTION_FINISHED; 298 connection_state_ = CONNECTION_FINISHED;
283 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) 299 for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i)
284 std::move(on_database_opened_callbacks_[i]).Run(); 300 std::move(on_database_opened_callbacks_[i]).Run();
285 on_database_opened_callbacks_.clear(); 301 on_database_opened_callbacks_.clear();
286 } 302 }
287 303
304 void LocalStorageContextMojo::DeleteAndRecreateDatabase() {
305 // For now don't support deletion and recreation when already connected.
306 DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
307
308 bool recreate_in_memory = false;
309
310 // If tried to recreate database on disk already, try again but this time
311 // in memory.
312 if (tried_to_recreate_ && !subdirectory_.empty()) {
313 recreate_in_memory = true;
314 } else if (tried_to_recreate_) {
315 // Give up completely, run without any database.
316 database_ = nullptr;
317 OnConnectionFinished();
318 return;
319 }
320
321 tried_to_recreate_ = true;
322
323 // Unit tests might not have a file_service_connection_, in which case there
324 // is nothing to rety.
325 if (!file_service_connection_) {
326 database_ = nullptr;
327 OnConnectionFinished();
328 return;
329 }
330
331 // Close and destroy database, and try again.
332 // TODO(mek): Somehow ensure closing is processed before Destroy is called.
Marijn Kruisselbrink 2017/01/11 19:10:58 This TODO is somewhat problematic, since I'm not s
jam 2017/01/12 17:24:42 If Destroy is on LevelDBInterface, won't there be
Marijn Kruisselbrink 2017/01/13 19:25:11 I suppose so, although it seemed less likely that
jam 2017/01/14 00:17:55 JavaScript support is being worked on :) Associat
333 database_ = nullptr;
334 if (directory_.is_bound()) {
335 leveldb_service_->Destroy(
336 std::move(directory_), "leveldb",
337 base::Bind(&LocalStorageContextMojo::OnDBDestroyed,
338 weak_ptr_factory_.GetWeakPtr(), recreate_in_memory));
339 } else {
340 // No directory, so nothing to destroy. Retrying to recreate will probably
341 // fail, but try anyway.
342 InitiateConnection(recreate_in_memory);
343 }
344 }
345
346 void LocalStorageContextMojo::OnDBDestroyed(
347 bool recreate_in_memory,
348 leveldb::mojom::DatabaseError status) {
349 // We're essentially ignoring the status here. Even if destroying failed we
350 // still want to go ahead and try to recreate.
351 InitiateConnection(recreate_in_memory);
352 }
353
288 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called 354 // The (possibly delayed) implementation of OpenLocalStorage(). Can be called
289 // directly from that function, or through |on_database_open_callbacks_|. 355 // directly from that function, or through |on_database_open_callbacks_|.
290 void LocalStorageContextMojo::BindLocalStorage( 356 void LocalStorageContextMojo::BindLocalStorage(
291 const url::Origin& origin, 357 const url::Origin& origin,
292 mojom::LevelDBWrapperRequest request) { 358 mojom::LevelDBWrapperRequest request) {
293 GetOrCreateDBWrapper(origin)->Bind(std::move(request)); 359 GetOrCreateDBWrapper(origin)->Bind(std::move(request));
294 } 360 }
295 361
296 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper( 362 LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper(
297 const url::Origin& origin) { 363 const url::Origin& origin) {
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 for (const auto& info : usage) { 430 for (const auto& info : usage) {
365 url::Origin origin_candidate(info.origin); 431 url::Origin origin_candidate(info.origin);
366 if (!origin_candidate.IsSameOriginWith(origin) && 432 if (!origin_candidate.IsSameOriginWith(origin) &&
367 origin_candidate.IsSamePhysicalOriginWith(origin)) 433 origin_candidate.IsSamePhysicalOriginWith(origin))
368 DeleteStorage(origin_candidate); 434 DeleteStorage(origin_candidate);
369 } 435 }
370 DeleteStorage(origin); 436 DeleteStorage(origin);
371 } 437 }
372 438
373 } // namespace content 439 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698