OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "extensions/browser/value_store/leveldb_value_store.h" | 5 #include "extensions/browser/value_store/leveldb_value_store.h" |
6 | 6 |
7 #include "base/files/file_util.h" | 7 #include "base/files/file_util.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
23 | 23 |
24 namespace util = value_store_util; | 24 namespace util = value_store_util; |
25 using content::BrowserThread; | 25 using content::BrowserThread; |
26 | 26 |
27 namespace { | 27 namespace { |
28 | 28 |
29 const char kInvalidJson[] = "Invalid JSON"; | 29 const char kInvalidJson[] = "Invalid JSON"; |
30 const char kCannotSerialize[] = "Cannot serialize value to JSON"; | 30 const char kCannotSerialize[] = "Cannot serialize value to JSON"; |
31 | 31 |
32 // UMA values used when recovering from a corrupted leveldb. | |
33 // Do not change/delete these values as you will break reporting for older | |
34 // copies of Chrome. Only add new values to the end. | |
35 enum LevelDBCorruptionRecoveryValue { | |
36 LEVELDB_RESTORE_DELETE_SUCCESS = 0, | |
37 LEVELDB_RESTORE_DELETE_FAILURE, | |
38 LEVELDB_RESTORE_REPAIR_SUCCESS, | |
39 LEVELDB_RESTORE_MAX | |
40 }; | |
41 | |
32 // Scoped leveldb snapshot which releases the snapshot on destruction. | 42 // Scoped leveldb snapshot which releases the snapshot on destruction. |
33 class ScopedSnapshot { | 43 class ScopedSnapshot { |
34 public: | 44 public: |
35 explicit ScopedSnapshot(leveldb::DB* db) | 45 explicit ScopedSnapshot(leveldb::DB* db) |
36 : db_(db), snapshot_(db->GetSnapshot()) {} | 46 : db_(db), snapshot_(db->GetSnapshot()) {} |
37 | 47 |
38 ~ScopedSnapshot() { | 48 ~ScopedSnapshot() { |
39 db_->ReleaseSnapshot(snapshot_); | 49 db_->ReleaseSnapshot(snapshot_); |
40 } | 50 } |
41 | 51 |
42 const leveldb::Snapshot* get() { | 52 const leveldb::Snapshot* get() { |
43 return snapshot_; | 53 return snapshot_; |
44 } | 54 } |
45 | 55 |
46 private: | 56 private: |
47 leveldb::DB* db_; | 57 leveldb::DB* db_; |
48 const leveldb::Snapshot* snapshot_; | 58 const leveldb::Snapshot* snapshot_; |
49 | 59 |
50 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot); | 60 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot); |
51 }; | 61 }; |
52 | 62 |
53 } // namespace | 63 } // namespace |
54 | 64 |
55 LeveldbValueStore::LeveldbValueStore(const std::string& uma_client_name, | 65 LeveldbValueStore::LeveldbValueStore(const std::string& uma_client_name, |
56 const base::FilePath& db_path) | 66 const base::FilePath& db_path) |
57 : db_path_(db_path), open_histogram_(nullptr) { | 67 : db_path_(db_path), open_histogram_(nullptr), restore_histogram_(nullptr) { |
58 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 68 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
59 | 69 |
60 // Used in lieu of UMA_HISTOGRAM_ENUMERATION because the histogram name is | 70 // Used in lieu of UMA_HISTOGRAM_ENUMERATION because the histogram name is |
61 // not a constant. | 71 // not a constant. |
62 open_histogram_ = base::LinearHistogram::FactoryGet( | 72 open_histogram_ = base::LinearHistogram::FactoryGet( |
63 "Extensions.Database.Open." + uma_client_name, 1, | 73 "Extensions.Database.Open." + uma_client_name, 1, |
64 leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1, | 74 leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1, |
65 base::Histogram::kUmaTargetedHistogramFlag); | 75 base::Histogram::kUmaTargetedHistogramFlag); |
76 restore_histogram_ = base::LinearHistogram::FactoryGet( | |
Devlin
2015/11/06 19:54:46
This is histogram black magic to me, so I'll trust
cmumford
2015/11/09 23:04:21
I manually hack-ed in a call to "restore_histogram
| |
77 "Extensions.Database.Restore." + uma_client_name, 1, LEVELDB_RESTORE_MAX, | |
78 LEVELDB_RESTORE_MAX + 1, base::Histogram::kUmaTargetedHistogramFlag); | |
66 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 79 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( |
67 this, "LeveldbValueStore", base::ThreadTaskRunnerHandle::Get()); | 80 this, "LeveldbValueStore", base::ThreadTaskRunnerHandle::Get()); |
68 } | 81 } |
69 | 82 |
70 LeveldbValueStore::~LeveldbValueStore() { | 83 LeveldbValueStore::~LeveldbValueStore() { |
71 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 84 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
72 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 85 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( |
73 this); | 86 this); |
74 | 87 |
75 // Delete the database from disk if it's empty (but only if we managed to | 88 // Delete the database from disk if it's empty (but only if we managed to |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
280 ValueStoreChange(next_key, next_value.release(), NULL)); | 293 ValueStoreChange(next_key, next_value.release(), NULL)); |
281 } | 294 } |
282 | 295 |
283 DeleteDbFile(); | 296 DeleteDbFile(); |
284 return MakeWriteResult(changes.Pass()); | 297 return MakeWriteResult(changes.Pass()); |
285 } | 298 } |
286 | 299 |
287 bool LeveldbValueStore::Restore() { | 300 bool LeveldbValueStore::Restore() { |
288 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 301 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
289 | 302 |
290 ReadResult result = Get(); | 303 // Possible to have a corrupted open database, so first close it. |
291 std::string previous_key; | 304 db_.reset(); |
292 while (result->IsCorrupted()) { | |
293 // If we don't have a specific corrupted key, or we've tried and failed to | |
294 // clear this specific key, or we fail to restore the key, then wipe the | |
295 // whole database. | |
296 if (!result->error().key.get() || *result->error().key == previous_key || | |
297 !RestoreKey(*result->error().key)) { | |
298 DeleteDbFile(); | |
299 result = Get(); | |
300 break; | |
301 } | |
302 | 305 |
303 // Otherwise, re-Get() the database to check if there is still any | 306 leveldb::Options options; |
304 // corruption. | 307 options.create_if_missing = true; |
305 previous_key = *result->error().key; | 308 |
306 result = Get(); | 309 // Repair can drop an unbounded number of leveldb tables (key/value sets) |
310 leveldb::Status status = leveldb::RepairDB(db_path_.AsUTF8Unsafe(), options); | |
311 if (status.ok()) { | |
312 restore_histogram_->Add(LEVELDB_RESTORE_REPAIR_SUCCESS); | |
313 return true; | |
307 } | 314 } |
308 | 315 |
309 // If we still have an error, it means we've tried deleting the database file, | 316 if (DeleteDbFile()) { |
310 // and failed. There's nothing more we can do. | 317 restore_histogram_->Add(LEVELDB_RESTORE_DELETE_SUCCESS); |
311 return !result->IsCorrupted(); | 318 return true; |
319 } | |
320 | |
321 restore_histogram_->Add(LEVELDB_RESTORE_DELETE_FAILURE); | |
322 return false; | |
312 } | 323 } |
313 | 324 |
314 bool LeveldbValueStore::RestoreKey(const std::string& key) { | 325 bool LeveldbValueStore::RestoreKey(const std::string& key) { |
315 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 326 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
316 | 327 |
317 ReadResult result = Get(key); | 328 ReadResult result = Get(key); |
318 if (result->IsCorrupted()) { | 329 if (result->IsCorrupted()) { |
319 leveldb::WriteBatch batch; | 330 leveldb::WriteBatch batch; |
320 batch.Delete(key); | 331 batch.Delete(key); |
321 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch); | 332 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
374 leveldb::Options options; | 385 leveldb::Options options; |
375 options.max_open_files = 0; // Use minimum. | 386 options.max_open_files = 0; // Use minimum. |
376 options.create_if_missing = true; | 387 options.create_if_missing = true; |
377 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; | 388 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; |
378 | 389 |
379 leveldb::DB* db = NULL; | 390 leveldb::DB* db = NULL; |
380 leveldb::Status status = | 391 leveldb::Status status = |
381 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db); | 392 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db); |
382 if (open_histogram_) | 393 if (open_histogram_) |
383 open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status)); | 394 open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status)); |
395 if (status.IsCorruption()) { | |
396 // Returning a corruption error should result in Restore() being called. | |
397 // However, since once a leveldb becomes corrupt it's unusable without | |
398 // some kind of repair or delete, so do that right now. | |
399 Restore(); | |
400 } | |
384 if (!status.ok()) | 401 if (!status.ok()) |
385 return ToValueStoreError(status, util::NoKey()); | 402 return ToValueStoreError(status, util::NoKey()); |
386 | 403 |
387 CHECK(db); | 404 CHECK(db); |
388 db_.reset(db); | 405 db_.reset(db); |
389 return util::NoError(); | 406 return util::NoError(); |
390 } | 407 } |
391 | 408 |
392 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb( | 409 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb( |
393 leveldb::ReadOptions options, | 410 leveldb::ReadOptions options, |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
461 | 478 |
462 it->SeekToFirst(); | 479 it->SeekToFirst(); |
463 bool is_empty = !it->Valid(); | 480 bool is_empty = !it->Valid(); |
464 if (!it->status().ok()) { | 481 if (!it->status().ok()) { |
465 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString(); | 482 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString(); |
466 return false; | 483 return false; |
467 } | 484 } |
468 return is_empty; | 485 return is_empty; |
469 } | 486 } |
470 | 487 |
471 void LeveldbValueStore::DeleteDbFile() { | 488 bool LeveldbValueStore::DeleteDbFile() { |
472 db_.reset(); // release any lock on the directory | 489 db_.reset(); // release any lock on the directory |
473 if (!base::DeleteFile(db_path_, true /* recursive */)) { | 490 if (!base::DeleteFile(db_path_, true /* recursive */)) { |
474 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " << | 491 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " << |
475 db_path_.value(); | 492 db_path_.value(); |
493 return false; | |
476 } | 494 } |
495 return true; | |
477 } | 496 } |
478 | 497 |
479 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError( | 498 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError( |
480 const leveldb::Status& status, | 499 const leveldb::Status& status, |
481 scoped_ptr<std::string> key) { | 500 scoped_ptr<std::string> key) { |
482 CHECK(!status.ok()); | 501 CHECK(!status.ok()); |
483 CHECK(!status.IsNotFound()); // not an error | 502 CHECK(!status.IsNotFound()); // not an error |
484 | 503 |
485 std::string message = status.ToString(); | 504 std::string message = status.ToString(); |
486 // The message may contain |db_path_|, which may be considered sensitive | 505 // The message may contain |db_path_|, which may be considered sensitive |
487 // data, and those strings are passed to the extension, so strip it out. | 506 // data, and those strings are passed to the extension, so strip it out. |
488 base::ReplaceSubstringsAfterOffset( | 507 base::ReplaceSubstringsAfterOffset( |
489 &message, 0u, db_path_.AsUTF8Unsafe(), "..."); | 508 &message, 0u, db_path_.AsUTF8Unsafe(), "..."); |
490 | 509 |
491 return Error::Create(CORRUPTION, message, key.Pass()); | 510 return Error::Create(CORRUPTION, message, key.Pass()); |
492 } | 511 } |
OLD | NEW |