Index: content/browser/dom_storage/local_storage_context_mojo.cc |
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc |
index 97636258a9d9801dfc5611635dec060029e38398..04d0108717e9ba6ad70926265cc8f3353b2ea082 100644 |
--- a/content/browser/dom_storage/local_storage_context_mojo.cc |
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc |
@@ -5,6 +5,8 @@ |
#include "content/browser/dom_storage/local_storage_context_mojo.h" |
#include "base/memory/ptr_util.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "components/leveldb/public/cpp/util.h" |
#include "content/browser/leveldb_wrapper_impl.h" |
#include "content/common/dom_storage/dom_storage_types.h" |
#include "services/file/public/interfaces/constants.mojom.h" |
@@ -13,8 +15,24 @@ |
namespace content { |
+// LevelDB database schema |
+// ======================= |
+// |
+// Version 1 (in sorted order): |
+// key: "VERSION" |
+// value: "1" |
+// |
+// key: "_" + <url::Origin> 'origin'> + '\x00' + <script controlled key> |
+// value: <script controlled value> |
+ |
namespace { |
+const char kVersionKey[] = "VERSION"; |
const char kOriginSeparator = '\x00'; |
+const char kDataPrefix[] = "_"; |
+const int64_t kMinSchemaVersion = 1; |
+const int64_t kCurrentSchemaVersion = 1; |
+ |
+void NoOpResult(leveldb::mojom::DatabaseError status) {} |
} |
LocalStorageContextMojo::LocalStorageContextMojo( |
@@ -72,6 +90,8 @@ void LocalStorageContextMojo::OpenLocalStorage( |
void LocalStorageContextMojo::SetDatabaseForTesting( |
leveldb::mojom::LevelDBDatabasePtr database) { |
+ DCHECK_EQ(connection_state_, NO_CONNECTION); |
+ connection_state_ = CONNECTION_IN_PROGRESS; |
database_ = std::move(database); |
OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); |
} |
@@ -125,7 +145,46 @@ void LocalStorageContextMojo::OnDatabaseOpened( |
directory_.reset(); |
file_system_.reset(); |
- // |leveldb_| should be known to either be valid or invalid by now. Run our |
+ // Verify DB schema version. |
+ if (database_) { |
+ database_->Get(leveldb::StdStringToUint8Vector(kVersionKey), |
+ base::Bind(&LocalStorageContextMojo::OnGotDatabaseVersion, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ return; |
+ } |
+ |
+ OnGotDatabaseVersion(leveldb::mojom::DatabaseError::IO_ERROR, |
+ std::vector<uint8_t>()); |
+} |
+ |
+void LocalStorageContextMojo::OnGotDatabaseVersion( |
+ leveldb::mojom::DatabaseError status, |
+ const std::vector<uint8_t>& value) { |
+ if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { |
+ // New database, write current version and continue. |
+ // TODO(mek): Delay writing version until first actual data gets committed. |
+ database_->Put(leveldb::StdStringToUint8Vector(kVersionKey), |
+ leveldb::StdStringToUint8Vector( |
+ base::Int64ToString(kCurrentSchemaVersion)), |
+ base::Bind(&NoOpResult)); |
+ // new database |
+ } else if (status == leveldb::mojom::DatabaseError::OK) { |
+ // Existing database, check if version number matches current schema |
+ // version. |
+ int64_t db_version; |
+ if (!base::StringToInt64(leveldb::Uint8VectorToStdString(value), |
+ &db_version) || |
+ db_version < kMinSchemaVersion || db_version > kCurrentSchemaVersion) { |
+ // TODO(mek): delete and recreate database, rather than failing outright. |
+ database_ = nullptr; |
+ } |
+ } else { |
+ // Other read error. Possibly database corruption. |
+ // TODO(mek): delete and recreate database, rather than failing outright. |
+ database_ = nullptr; |
+ } |
+ |
+ // |database_| should be known to either be valid or invalid by now. Run our |
// delayed bindings. |
connection_state_ = CONNECTION_FINISHED; |
for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) |
@@ -150,7 +209,7 @@ void LocalStorageContextMojo::BindLocalStorage( |
auto found = level_db_wrappers_.find(origin); |
if (found == level_db_wrappers_.end()) { |
level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>( |
- database_.get(), origin.Serialize() + kOriginSeparator, |
+ database_.get(), kDataPrefix + origin.Serialize() + kOriginSeparator, |
kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, |
base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, |
kMaxCommitsPerHour, |