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

Side by Side Diff: webkit/dom_storage/session_storage_database.cc

Issue 9963107: Persist sessionStorage on disk. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup. Created 8 years, 8 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 | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698