Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/dom_storage/session_storage_database.h" | |
| 6 | |
| 7 #include "base/file_util.h" | |
| 8 #include "base/stringprintf.h" | |
| 9 #include "base/string_number_conversions.h" | |
| 10 #include "base/utf_string_conversions.h" | |
| 11 #include "googleurl/src/gurl.h" | |
| 12 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
| 13 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | |
| 14 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
| 15 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
| 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
| 17 | |
| 18 // Layout of the database: | |
| 19 // | key | value | | |
| 20 // ----------------------------------------------------------------------- | |
| 21 // | map-1 | 2 (refcount, start of map-1-* keys)| | |
| 22 // | map-1-a | b (a = b in map 1) | | |
| 23 // | ... | | | |
| 24 // | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)| | |
| 25 // | namespace-1-origin1 | 1 (mapid) | | |
| 26 // | namespace-1-origin2 | 2 | | |
| 27 // | namespace-2 | dummy | | |
| 28 // | namespace-2-origin1 | 1 (shallow copy) | | |
| 29 // | namespace-2-origin2 | 2 (shallow copy) | | |
| 30 // | namespace-3 | dummy | | |
| 31 // | namespace-3-origin1 | 3 (deep copy) | | |
| 32 // | namespace-3-origin2 | 2 (shallow copy) | | |
| 33 // | next-namespace-id | 4 | | |
| 34 // | next-map-id | 4 | | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 // Helper functions for creating the keys needed for the schema. | |
| 39 std::string NamespaceStartKey(const std::string& namespace_id) { | |
| 40 return base::StringPrintf("namespace-%s", namespace_id.c_str()); | |
| 41 } | |
| 42 | |
| 43 std::string NamespaceStartKey(int64 namespace_id, int64 namespace_offset) { | |
| 44 return NamespaceStartKey( | |
| 45 base::Int64ToString(namespace_id + namespace_offset)); | |
| 46 } | |
| 47 | |
| 48 std::string NamespaceKey(const std::string& namespace_id, | |
| 49 const std::string& origin) { | |
| 50 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(), | |
| 51 origin.c_str()); | |
| 52 } | |
| 53 | |
| 54 std::string NamespaceKey(int64 namespace_id, int64 namespace_offset, | |
| 55 const GURL& origin) { | |
| 56 return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset), | |
| 57 origin.spec()); | |
| 58 } | |
| 59 | |
| 60 std::string NamespacePrefix() { | |
| 61 return std::string("namespace-"); | |
|
michaeln
2012/04/22 22:50:01
nit: indent
marja
2012/04/23 14:38:25
Done (in codereview.chromium.org/10176005 ).
| |
| 62 } | |
| 63 | |
| 64 std::string MapRefCountKey(const std::string& map_id) { | |
| 65 return base::StringPrintf("map-%s", map_id.c_str()); | |
| 66 } | |
| 67 | |
| 68 std::string MapKey(const std::string& map_id, const std::string& key) { | |
| 69 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); | |
| 70 } | |
| 71 | |
| 72 std::string MapPrefix() { | |
| 73 return std::string("map-"); | |
| 74 } | |
| 75 | |
| 76 std::string NextNamespaceIdKey() { | |
| 77 return "next-namespace-id"; | |
| 78 } | |
| 79 | |
| 80 std::string NextMapIdKey() { | |
| 81 return "next-map-id"; | |
| 82 } | |
| 83 | |
| 84 } // namespace | |
| 85 | |
| 86 namespace dom_storage { | |
| 87 | |
| 88 SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path) | |
| 89 : file_path_(file_path), | |
| 90 failed_to_open_(false), | |
| 91 namespace_offset_(0) { } | |
| 92 | |
| 93 SessionStorageDatabase::~SessionStorageDatabase() { } | |
| 94 | |
| 95 void SessionStorageDatabase::ReadAllValues(int64 namespace_id, | |
| 96 const GURL& origin, | |
| 97 ValuesMap* result) { | |
| 98 // We don't create a database if it doesn't exist. In that case, there is | |
| 99 // nothing to be added to the result. | |
| 100 if (!LazyOpen(false)) | |
| 101 return; | |
| 102 // Check if there is map for |namespace_id| and |origin|. | |
| 103 std::string namespace_key = | |
| 104 NamespaceKey(namespace_id, namespace_offset_, origin); | |
| 105 std::string map_id; | |
| 106 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
| 107 if (!s.ok() || map_id.empty()) | |
| 108 return; | |
| 109 ReadMap(map_id, result, false); | |
| 110 } | |
| 111 | |
| 112 bool SessionStorageDatabase::CommitChanges(int64 namespace_id, | |
| 113 const GURL& origin, | |
| 114 bool clear_all_first, | |
| 115 const ValuesMap& changes) { | |
| 116 // Even if |changes| is empty, we need to write the appropriate placeholders | |
| 117 // in the database, so that it can be later shallow-copied succssfully. | |
| 118 if (!LazyOpen(true)) | |
| 119 return false; | |
| 120 leveldb::WriteBatch batch; | |
| 121 | |
| 122 // Ensure that the key namespace-N (see the schema above) exists. | |
| 123 batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), ""); | |
| 124 | |
| 125 // Ensure that the next namespace id is up to date. | |
| 126 UpdateNextNamespaceId(namespace_id, &batch); | |
| 127 | |
| 128 // Write the data into the map. | |
| 129 std::string namespace_key = | |
| 130 NamespaceKey(namespace_id, namespace_offset_, origin); | |
| 131 std::string map_id; | |
| 132 leveldb::Status s; | |
| 133 s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
| 134 DCHECK(s.ok() || s.IsNotFound()); | |
| 135 // The map for |namespace_id| and |origin| needs to be created if there is | |
| 136 // data to be written to it. | |
| 137 if (s.IsNotFound() && !changes.empty()) | |
| 138 CreateNewMap(namespace_key, &batch, &map_id); | |
| 139 else if (clear_all_first) | |
| 140 ClearMap(map_id, &batch); | |
| 141 | |
| 142 WriteValuesToMap(map_id, changes, &batch); | |
| 143 | |
| 144 s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 145 DCHECK(s.ok()); | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id, | |
| 150 int64 new_namespace_id) { | |
| 151 // Go through all origins in the namespace |namespace_id|, create placeholders | |
| 152 // for them in |new_namespace_id|, and associate them with the existing maps. | |
| 153 | |
| 154 // Example, data before shallow copy: | |
| 155 // | map-1 | 1 (refcount) | | |
| 156 // | map-1-a | b | | |
| 157 // | namespace-1 (1 = namespace id) | dummy | | |
| 158 // | namespace-1-origin1 | 1 (mapid) | | |
| 159 | |
| 160 // Example, data after shallow copy: | |
| 161 // | map-1 | 2 (inc. refcount) | | |
| 162 // | map-1-a | b | | |
| 163 // | namespace-1 (1 = namespace id) | dummy | | |
| 164 // | namespace-1-origin1 | 1 (mapid) | | |
| 165 // | namespace-2 | dummy | | |
| 166 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
| 167 | |
| 168 if (!LazyOpen(true)) { | |
| 169 return false; | |
| 170 } | |
| 171 leveldb::WriteBatch batch; | |
| 172 batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), ""); | |
| 173 | |
| 174 // Ensure that the next namespace id is up to date. | |
| 175 UpdateNextNamespaceId(new_namespace_id, &batch); | |
| 176 | |
| 177 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
| 178 std::string namespace_start_key = | |
| 179 NamespaceStartKey(namespace_id, namespace_offset_); | |
| 180 it->Seek(namespace_start_key); | |
| 181 if (!it->Valid()) { | |
| 182 // It's possible that the namespace doesn't contain any data. (This happens | |
| 183 // when the namespace doesn't contain any areas.) We don't need to do | |
| 184 // anything. | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins. | |
| 189 for (it->Next(); it->Valid(); it->Next()) { | |
| 190 std::string key = it->key().ToString(); | |
| 191 if (key.find(namespace_start_key) != 0) { | |
| 192 // Iterated past the origins for this namespace. | |
| 193 break; | |
| 194 } | |
| 195 std::string map_id = it->value().ToString(); | |
| 196 // Increase the ref count for the map. | |
| 197 std::string map_key = MapRefCountKey(map_id); | |
| 198 int64 old_ref_count = 0; | |
| 199 bool success = GetRefCount(map_key, &old_ref_count); | |
| 200 if (!success) | |
| 201 return false; | |
| 202 batch.Put(map_key, base::Int64ToString(++old_ref_count)); | |
| 203 // Associate the map with the new namespace. | |
| 204 size_t second_dash = key.find('-', namespace_start_key.length()); | |
| 205 std::string origin = key.substr(second_dash + 1); | |
| 206 std::string new_namespace_key = NamespaceKey( | |
| 207 base::Int64ToString(new_namespace_id + namespace_offset_), origin); | |
| 208 batch.Put(new_namespace_key, map_id); | |
| 209 } | |
| 210 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 211 DCHECK(s.ok()); | |
| 212 return true; | |
| 213 } | |
| 214 | |
| 215 bool SessionStorageDatabase::DeepCopy(int64 namespace_id, | |
| 216 const GURL& origin) { | |
| 217 // Example, data before deep copy: | |
| 218 // | namespace-1 (1 = namespace id) | dummy | | |
| 219 // | namespace-1-origin1 | 1 (mapid) | | |
| 220 // | namespace-2 | dummy | | |
| 221 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
| 222 // | map-1 | 2 (refcount) | | |
| 223 // | map-1-a | b | | |
| 224 | |
| 225 // Example, data after deep copy copy: | |
| 226 // | namespace-1 (1 = namespace id) | dummy | | |
| 227 // | namespace-1-origin1 | 1 (mapid) | | |
| 228 // | namespace-2 | dummy | | |
| 229 // | namespace-2-origin1 | 2 (mapid) << references the new map | |
| 230 // | map-1 | 1 (dec. refcount) | | |
| 231 // | map-1-a | b | | |
| 232 // | map-2 | 1 (refcount) | | |
| 233 // | map-2-a | b | | |
| 234 | |
| 235 if (!LazyOpen(true)) { | |
| 236 return false; | |
| 237 } | |
| 238 leveldb::Status s; | |
| 239 std::string namespace_key = | |
| 240 NamespaceKey(namespace_id, namespace_offset_, origin); | |
| 241 leveldb::WriteBatch batch; | |
| 242 std::string old_map_id; | |
| 243 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); | |
| 244 DCHECK(s.ok()); | |
| 245 | |
| 246 // If this copy is the the only "shallow" copy of the map, no deep copying is | |
| 247 // needed. | |
| 248 int64 ref_count = 0; | |
| 249 bool success = GetRefCount(MapRefCountKey(old_map_id), &ref_count); | |
| 250 if (!success) | |
| 251 return false; | |
| 252 if (ref_count == 1) | |
| 253 return true; | |
| 254 | |
| 255 std::string new_map_id; | |
| 256 CreateNewMap(namespace_key, &batch, &new_map_id); | |
| 257 | |
| 258 // Copy the values in the map. | |
| 259 ValuesMap values; | |
| 260 ReadMap(old_map_id, &values, false); | |
| 261 WriteValuesToMap(new_map_id, values, &batch); | |
| 262 | |
| 263 DecreaseRefCount(old_map_id, &batch); | |
| 264 | |
| 265 s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 266 DCHECK(s.ok()); | |
| 267 return true; | |
| 268 } | |
| 269 | |
| 270 bool SessionStorageDatabase::DisassociateMap(int64 namespace_id, | |
| 271 const GURL& origin) { | |
| 272 if (!LazyOpen(true)) { | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 leveldb::Status s; | |
| 277 std::string namespace_key = | |
| 278 NamespaceKey(namespace_id, namespace_offset_, origin); | |
| 279 leveldb::WriteBatch batch; | |
| 280 std::string old_map_id; | |
| 281 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); | |
|
michaeln
2012/04/22 22:50:01
why is it ok to not update the value is namespace_
marja
2012/04/23 15:11:36
This was wrong, fixed in codereview.chromium.org/1
| |
| 282 DCHECK(s.ok() || s.IsNotFound()); | |
| 283 if (s.ok()) | |
| 284 DecreaseRefCount(old_map_id, &batch); | |
| 285 s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 286 DCHECK(s.ok()); | |
| 287 return true; | |
| 288 } | |
| 289 | |
| 290 void SessionStorageDatabase::DeleteOrigin(int64 namespace_id, | |
| 291 const GURL& origin) { | |
| 292 if (!LazyOpen(false)) { | |
| 293 // No need to create the database if it doesn't exist. | |
| 294 return; | |
| 295 } | |
| 296 leveldb::WriteBatch batch; | |
| 297 DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch); | |
| 298 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 299 DCHECK(s.ok()); | |
| 300 } | |
| 301 | |
| 302 void SessionStorageDatabase::DeleteNamespace(int64 namespace_id) { | |
| 303 if (!LazyOpen(false)) { | |
| 304 // No need to create the database if it doesn't exist. | |
| 305 return; | |
| 306 } | |
| 307 leveldb::WriteBatch batch; | |
| 308 DeleteNamespace(base::Int64ToString(namespace_id), &batch); | |
| 309 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 310 DCHECK(s.ok()); | |
| 311 } | |
| 312 | |
| 313 void SessionStorageDatabase::DeleteLeftoverData() { | |
| 314 if (!LazyOpen(false)) { | |
| 315 // No need to create the database if it doesn't exist. | |
| 316 return; | |
| 317 } | |
| 318 leveldb::WriteBatch batch; | |
| 319 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
|
michaeln
2012/04/22 22:50:01
should we start the iteration at the 'namespace-'
marja
2012/04/23 14:38:25
Done (in codereview.chromium.org/10176005 ).
| |
| 320 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
| 321 // If the key is of the form "namespace-<namespaceid>", delete the | |
| 322 // corresponding namespace. | |
| 323 std::string key = it->key().ToString(); | |
| 324 std::string namespace_prefix = NamespacePrefix(); | |
| 325 if (key.find(namespace_prefix) != 0) { | |
| 326 continue; | |
| 327 } | |
| 328 size_t second_dash = key.find('-', namespace_prefix.length()); | |
| 329 if (second_dash == std::string::npos) { | |
| 330 std::string namespace_id = key.substr(namespace_prefix.length()); | |
| 331 DeleteNamespace(namespace_id, &batch); | |
| 332 } | |
| 333 } | |
| 334 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
| 335 DCHECK(s.ok()); | |
| 336 } | |
| 337 | |
| 338 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | |
| 339 if (failed_to_open_) { | |
| 340 // Don't try to open a database that we know has failed | |
| 341 // already. | |
| 342 return false; | |
| 343 } | |
| 344 | |
| 345 if (IsOpen()) | |
| 346 return true; | |
| 347 | |
| 348 bool directory_exists = file_util::PathExists(file_path_); | |
| 349 | |
| 350 if (!directory_exists && !create_if_needed) { | |
| 351 // If the directory doesn't exist already and we haven't been asked to | |
| 352 // create a file on disk, then we don't bother opening the database. This | |
| 353 // means we wait until we absolutely need to put something onto disk before | |
| 354 // we do so. | |
| 355 return false; | |
| 356 } | |
| 357 | |
| 358 leveldb::DB* db; | |
| 359 leveldb::Status s = TryToOpen(file_path_, &db); | |
| 360 if (!s.ok()) { | |
| 361 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
| 362 << ", error: " << s.ToString(); | |
| 363 DCHECK(db == NULL); | |
| 364 | |
| 365 // Clear the directory and try again. | |
| 366 file_util::Delete(file_path_, true); | |
| 367 s = TryToOpen(file_path_, &db); | |
| 368 if (!s.ok()) { | |
| 369 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
| 370 << ", error: " << s.ToString(); | |
| 371 DCHECK(db == NULL); | |
| 372 failed_to_open_ = true; | |
| 373 return false; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 db_.reset(db); | |
| 378 | |
| 379 bool success = GetNextNamespaceId(&namespace_offset_); | |
| 380 DCHECK(success); | |
| 381 | |
| 382 return true; | |
| 383 } | |
| 384 | |
| 385 leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path, | |
| 386 leveldb::DB** db) { | |
| 387 leveldb::Options options; | |
| 388 // The directory exists but a valid leveldb database might not exist inside it | |
| 389 // (e.g., a subset of the needed files might be missing). Handle this | |
| 390 // situation gracefully by creating the database now. | |
| 391 options.create_if_missing = true; | |
| 392 #if defined(OS_WIN) | |
| 393 return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db); | |
| 394 #elif defined(OS_POSIX) | |
| 395 return leveldb::DB::Open(options, file_path.value(), db); | |
| 396 #endif | |
| 397 } | |
| 398 | |
| 399 bool SessionStorageDatabase::IsOpen() const { | |
| 400 return db_.get(); | |
| 401 } | |
| 402 | |
| 403 void SessionStorageDatabase::ReadMap(const std::string& map_id, | |
| 404 ValuesMap* result, | |
| 405 bool only_keys) { | |
| 406 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
| 407 std::string map_start_key = MapRefCountKey(map_id); | |
| 408 it->Seek(map_start_key); | |
| 409 if (!it->Valid()) | |
| 410 return; | |
| 411 // Skip the dummy entry "map-<mapid>". | |
| 412 for (it->Next(); it->Valid(); it->Next()) { | |
| 413 // Key is of the form "map-<mapid>-<key>". | |
| 414 std::string key = it->key().ToString(); | |
| 415 int prefix_length = MapPrefix().length(); | |
| 416 size_t second_dash = key.find('-', prefix_length); | |
| 417 if (second_dash == std::string::npos || | |
| 418 key.substr(prefix_length, second_dash - prefix_length) != map_id) { | |
| 419 // Iterated beyond the keys in this map. | |
| 420 break; | |
| 421 } | |
| 422 string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1)); | |
| 423 if (only_keys) { | |
| 424 (*result)[key16] = NullableString16(true); | |
| 425 } else { | |
| 426 // Convert the raw data stored in std::string (it->value()) to raw data | |
| 427 // stored in string16. | |
| 428 // FIXME(marja): Add tests. | |
| 429 string16 value; | |
| 430 size_t len = it->value().size() / sizeof(char16); | |
| 431 value.resize(len); | |
| 432 value.assign(reinterpret_cast<const char16*>(it->value().data()), len); | |
| 433 (*result)[key16] = NullableString16(value, false); | |
| 434 } | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key, | |
| 439 leveldb::WriteBatch* batch, | |
| 440 std::string* map_id) { | |
| 441 // Create a new map. | |
| 442 std::string next_map_id_key = NextMapIdKey(); | |
| 443 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id); | |
| 444 DCHECK(s.ok() || s.IsNotFound()); | |
| 445 int64 next_map_id = 0; | |
| 446 if (s.IsNotFound()) { | |
| 447 *map_id = "0"; | |
| 448 } else { | |
| 449 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id); | |
| 450 DCHECK(conversion_ok); | |
| 451 // FIXME(marja): What to do if the database is corrupt? | |
| 452 } | |
| 453 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); | |
| 454 batch->Put(namespace_key, *map_id); | |
| 455 batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1)); | |
| 456 return true; | |
| 457 } | |
| 458 | |
| 459 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id, | |
| 460 const ValuesMap& values, | |
| 461 leveldb::WriteBatch* batch) { | |
| 462 for(ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) { | |
| 463 NullableString16 value = it->second; | |
| 464 std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); | |
| 465 if (value.is_null()) { | |
| 466 batch->Delete(key); | |
| 467 } else { | |
| 468 // Convert the raw data stored in string16 to raw data stored in | |
| 469 // std::string. | |
| 470 // FIXME(marja): Add tests. | |
| 471 const char* data = reinterpret_cast<const char*>(value.string().data()); | |
| 472 size_t size = value.string().size() * 2; | |
| 473 batch->Put(key, std::string(data, size)); | |
| 474 } | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) { | |
| 479 std::string next_namespace_id_string; | |
| 480 leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(), | |
| 481 &next_namespace_id_string); | |
| 482 DCHECK(s.ok() || s.IsNotFound()); | |
| 483 if (s.IsNotFound()) { | |
| 484 *next_namespace_id = 0; | |
| 485 return true; | |
| 486 } | |
| 487 bool conversion_ok = | |
| 488 base::StringToInt64(next_namespace_id_string, next_namespace_id); | |
| 489 DCHECK(conversion_ok); | |
| 490 return conversion_ok; | |
| 491 } | |
| 492 | |
| 493 void SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id, | |
| 494 leveldb::WriteBatch* batch) { | |
| 495 int64 next_namespace_id; | |
| 496 bool success = GetNextNamespaceId(&next_namespace_id); | |
| 497 DCHECK(success); | |
| 498 if (next_namespace_id < namespace_id + namespace_offset_ + 1) { | |
| 499 next_namespace_id = namespace_id + namespace_offset_ + 1; | |
| 500 batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id)); | |
| 501 } | |
| 502 } | |
| 503 | |
| 504 bool SessionStorageDatabase::GetRefCount(const std::string& map_key, | |
| 505 int64* ref_count) { | |
| 506 std::string ref_count_string; | |
| 507 leveldb::Status s = | |
| 508 db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string); | |
| 509 DCHECK(s.ok()); | |
| 510 if (s.ok()) { | |
| 511 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count); | |
| 512 DCHECK(conversion_ok); | |
| 513 return conversion_ok; | |
| 514 } | |
| 515 return false; | |
| 516 } | |
| 517 | |
| 518 void SessionStorageDatabase::DecreaseRefCount(const std::string& map_id, | |
| 519 leveldb::WriteBatch* batch) { | |
| 520 // Decrease the ref count for the map. | |
| 521 std::string map_key = MapRefCountKey(map_id); | |
| 522 int64 ref_count = 0; | |
| 523 bool success = GetRefCount(map_key, &ref_count); | |
| 524 DCHECK(success); | |
| 525 if (!success) | |
| 526 return; | |
| 527 if (--ref_count > 0) { | |
| 528 batch->Put(map_key, base::Int64ToString(ref_count)); | |
| 529 } else { | |
| 530 // Clear all keys in the map. | |
| 531 ClearMap(map_id, batch); | |
| 532 batch->Delete(map_key); | |
| 533 } | |
| 534 } | |
| 535 | |
| 536 void SessionStorageDatabase::ClearMap(const std::string& map_id, | |
| 537 leveldb::WriteBatch* batch) { | |
| 538 ValuesMap values; | |
| 539 ReadMap(map_id, &values, true); | |
| 540 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) | |
| 541 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); | |
| 542 } | |
| 543 | |
| 544 void SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id, | |
| 545 const std::string& origin, | |
| 546 leveldb::WriteBatch* batch) { | |
| 547 std::string namespace_key = NamespaceKey(namespace_id, origin); | |
| 548 std::string map_id; | |
| 549 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
| 550 DCHECK(s.ok() || s.IsNotFound()); | |
| 551 if (s.IsNotFound()) | |
| 552 return; // Nothing to delete. | |
| 553 DecreaseRefCount(map_id, batch); | |
|
michaeln
2012/04/22 22:50:01
not sure this will do what you want it to do, a le
marja
2012/04/23 14:38:25
Ahh, I see, thanks for catching this. Things go wr
| |
| 554 batch->Delete(namespace_key); | |
| 555 } | |
| 556 | |
| 557 void SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id, | |
| 558 leveldb::WriteBatch* batch) { | |
| 559 std::string namespace_start_key = NamespaceStartKey(namespace_id); | |
| 560 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
| 561 it->Seek(namespace_start_key); | |
| 562 if (!it->Valid()) | |
| 563 return; | |
| 564 // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in | |
| 565 // the namespace. | |
| 566 for (it->Next(); it->Valid(); it->Next()) { | |
| 567 // Key is of the form "namespace-<namespaceid>-<origin>". | |
| 568 std::string key = it->key().ToString(); | |
| 569 std::string namespace_prefix = NamespacePrefix(); | |
| 570 if (key.find(namespace_prefix) != 0) { | |
| 571 // Iterated beyond the keys for this namespace. | |
| 572 break; | |
| 573 } | |
| 574 int prefix_length = namespace_prefix.length(); | |
| 575 size_t second_dash = key.find('-', prefix_length); | |
| 576 if (second_dash == std::string::npos || | |
| 577 key.substr(prefix_length, | |
| 578 second_dash - prefix_length) != namespace_id) { | |
| 579 // Iterated beyond the keys for this namespace. | |
| 580 break; | |
| 581 } | |
| 582 std::string origin = key.substr(second_dash + 1); | |
| 583 DeleteOrigin(namespace_id, origin, batch); | |
| 584 } | |
| 585 batch->Delete(namespace_start_key); | |
| 586 } | |
| 587 | |
| 588 // FIXME(marja): Remove this (or dump more intelligently). | |
| 589 void SessionStorageDatabase::DumpData() { | |
| 590 LOG(WARNING) << "Dumping session storage"; | |
| 591 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
| 592 if (LazyOpen(false)) { | |
| 593 for (it->SeekToFirst(); it->Valid(); it->Next()) | |
| 594 LOG(WARNING) << it->key().ToString() << ": " << it->value().ToString(); | |
| 595 } | |
| 596 LOG(WARNING) << "Dumping session storage complete"; | |
| 597 } | |
| 598 | |
| 599 bool SessionStorageDatabase::CheckConsistency() { | |
| 600 if (!LazyOpen(false)) | |
| 601 return true; | |
| 602 std::map<int64, int64> found_map_references; | |
| 603 std::string namespace_prefix = NamespacePrefix(); | |
| 604 std::set<int64> found_namespace_ids; | |
| 605 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
| 606 int64 max_map_id = -1; | |
| 607 int64 max_namespace_id = -1; | |
| 608 // First iterate "namespace-" keys. | |
| 609 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
| 610 std::string key = it->key().ToString(); | |
| 611 if (key.find(namespace_prefix) != 0) | |
| 612 continue; | |
| 613 size_t second_dash = key.find('-', namespace_prefix.length()); | |
| 614 if (second_dash == std::string::npos) { | |
| 615 // Key is of the form "namespace-<namespaceid>". | |
| 616 std::string namespace_id_str = key.substr(namespace_prefix.length()); | |
| 617 int64 namespace_id; | |
| 618 bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); | |
| 619 if (!conversion_ok) { | |
| 620 LOG(WARNING) << "Invalid key: " << key; | |
| 621 LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; | |
| 622 return false; | |
| 623 } | |
| 624 found_namespace_ids.insert(namespace_id); | |
| 625 if (namespace_id > max_namespace_id) | |
| 626 max_namespace_id = namespace_id; | |
| 627 } else { | |
| 628 // Key is of the form "namespace-<namespaceid>-<origin>", and the value | |
| 629 // is the map id. | |
| 630 std::string namespace_id_str = | |
| 631 key.substr(namespace_prefix.length(), | |
| 632 second_dash - namespace_prefix.length()); | |
| 633 int64 namespace_id; | |
| 634 bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); | |
| 635 if (!conversion_ok) { | |
| 636 LOG(WARNING) << "Invalid key: " << key; | |
| 637 LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; | |
| 638 return false; | |
| 639 } | |
| 640 if (found_namespace_ids.find(namespace_id) == found_namespace_ids.end()) { | |
| 641 LOG(WARNING) << "Key " << key << " not attached to a namespace entry"; | |
| 642 return false; | |
| 643 } | |
| 644 | |
| 645 int64 map_id; | |
| 646 conversion_ok = base::StringToInt64(it->value().ToString(), &map_id); | |
| 647 if (!conversion_ok) { | |
| 648 LOG(WARNING) << "Invalid data: " << key << ": " | |
| 649 << it->value().ToString(); | |
| 650 LOG(WARNING) << "Cannot convert the value to map id."; | |
| 651 return false; | |
| 652 } | |
| 653 ++found_map_references[map_id]; | |
| 654 if (map_id > max_map_id) | |
| 655 max_map_id = map_id; | |
| 656 } | |
| 657 } | |
| 658 // Then iterate "map-" keys. | |
| 659 std::string map_prefix = MapPrefix(); | |
| 660 std::set<int64> found_map_ids; | |
| 661 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
| 662 std::string key = it->key().ToString(); | |
| 663 if (key.find(map_prefix) != 0) | |
| 664 continue; | |
| 665 size_t second_dash = key.find('-', map_prefix.length()); | |
| 666 if (second_dash == std::string::npos) { | |
| 667 // Key is of the form "map-<mapid>" and the value is the ref count. | |
| 668 std::string map_id_str = key.substr(map_prefix.length(), second_dash); | |
| 669 int64 map_id; | |
| 670 bool conversion_ok = base::StringToInt64(map_id_str, &map_id); | |
| 671 if (!conversion_ok) { | |
| 672 LOG(WARNING) << "Invalid key: " << key; | |
| 673 LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; | |
| 674 return false; | |
| 675 } | |
| 676 // Check that the map is not stale. | |
| 677 if (found_map_references.find(map_id) == found_map_references.end()) { | |
| 678 LOG(WARNING) << "Unattached map: " << key; | |
| 679 return false; | |
| 680 } | |
| 681 int64 ref_count; | |
| 682 conversion_ok = base::StringToInt64( | |
| 683 it->value().ToString(), &ref_count); | |
| 684 if (!conversion_ok) { | |
| 685 LOG(WARNING) << "Invalid data: " << key << ": " | |
| 686 << it->value().ToString(); | |
| 687 LOG(WARNING) << "Cannot convert the value to int64."; | |
| 688 return false; | |
| 689 } | |
| 690 if (found_map_references[map_id] != ref_count) { | |
| 691 LOG(WARNING) << "Invalid ref count: " << key << ": " | |
| 692 << it->value().ToString(); | |
| 693 LOG(WARNING) << "Should be " << found_map_references[map_id]; | |
| 694 } | |
| 695 // Mark the map as existing. | |
| 696 found_map_references[map_id] = -1; | |
| 697 found_map_ids.insert(map_id); | |
| 698 } else { | |
| 699 // Key is of the form "map-<mapid>-key". Check that the entry for | |
| 700 // "map-<mapid>" exists. | |
| 701 std::string map_id_str = | |
| 702 key.substr(map_prefix.length(), second_dash - map_prefix.length()); | |
| 703 int64 map_id; | |
| 704 bool conversion_ok = base::StringToInt64(map_id_str, &map_id); | |
| 705 if (!conversion_ok) { | |
| 706 LOG(WARNING) << "Invalid key: " << key; | |
| 707 LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; | |
| 708 return false; | |
| 709 } | |
| 710 if (found_map_ids.find(map_id) == found_map_ids.end()) { | |
| 711 LOG(WARNING) << "Key " << key << " not attached to a map entry"; | |
| 712 return false; | |
| 713 } | |
| 714 } | |
| 715 } | |
| 716 // Check the next map id key. | |
| 717 std::string next_map_id_key = NextMapIdKey(); | |
| 718 std::string next_map_id_str; | |
| 719 leveldb::Status s = | |
| 720 db_->Get(leveldb::ReadOptions(), next_map_id_key, &next_map_id_str); | |
| 721 if (!s.ok() && !s.IsNotFound()) { | |
| 722 LOG(WARNING) << "Error reading " << next_map_id_key; | |
| 723 return false; | |
| 724 } | |
| 725 int64 next_map_id = 0; | |
| 726 if (s.ok()) { | |
| 727 bool conversion_ok = base::StringToInt64(next_map_id_str, &next_map_id); | |
| 728 if (!conversion_ok) { | |
| 729 LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " | |
| 730 << next_map_id_str; | |
| 731 LOG(WARNING) << "Cannot convert the value to map id."; | |
| 732 return false; | |
| 733 } | |
| 734 if (next_map_id <= max_map_id) { | |
| 735 LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " | |
| 736 << next_map_id_str; | |
| 737 LOG(WARNING) << "Should be at least " << max_map_id + 1; | |
| 738 return false; | |
| 739 } | |
| 740 } else { | |
| 741 // Not having the next map id key is only ok if there are no maps. | |
| 742 if (max_map_id != -1) { | |
| 743 LOG(WARNING) << "No next map id key, but maps exist."; | |
| 744 return false; | |
| 745 } | |
| 746 } | |
| 747 // Check the next namespace id key. | |
| 748 std::string next_namespace_id_key = NextNamespaceIdKey(); | |
| 749 std::string next_namespace_id_str; | |
| 750 s = db_->Get(leveldb::ReadOptions(), next_namespace_id_key, | |
| 751 &next_namespace_id_str); | |
| 752 if (!s.ok() && !s.IsNotFound()) { | |
| 753 LOG(WARNING) << "Error reading " << next_namespace_id_key; | |
| 754 return false; | |
| 755 } | |
| 756 int64 next_namespace_id = 0; | |
| 757 if (s.ok()) { | |
| 758 bool conversion_ok = | |
| 759 base::StringToInt64(next_namespace_id_str, &next_namespace_id); | |
| 760 if (!conversion_ok) { | |
| 761 LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key | |
| 762 << ": " << next_namespace_id_str; | |
| 763 LOG(WARNING) << "Cannot convert the value to namespace id."; | |
| 764 return false; | |
| 765 } | |
| 766 if (next_namespace_id <= max_namespace_id) { | |
| 767 LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key | |
| 768 << ": " << next_namespace_id_str; | |
| 769 LOG(WARNING) << "Should be at least " << max_namespace_id + 1; | |
| 770 return false; | |
| 771 } | |
| 772 } else { | |
| 773 // Not having the next namespace id key is only ok if there are no | |
| 774 // namespaces. | |
| 775 if (max_namespace_id != -1) { | |
| 776 LOG(WARNING) << "No next namespace id key, but namespaces exist."; | |
| 777 return false; | |
| 778 } | |
| 779 } | |
| 780 | |
| 781 // Check that all maps referred to exist. | |
| 782 for (std::map<int64, int64>::const_iterator it = found_map_references.begin(); | |
| 783 it != found_map_references.end(); ++it) { | |
| 784 if (it->second != -1) { | |
| 785 LOG(WARNING) << "Nonexisting map: " << it->first; | |
| 786 return false; | |
| 787 } | |
| 788 } | |
| 789 return true; | |
| 790 } | |
| 791 | |
| 792 } // namespace dom_storage | |
| OLD | NEW |